mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Migrate FancyZones data persisting from Registry to JSON file (#1194)
* Migrate FancyZones data persisting from Registry to JSON file * Address PR comment: Remove redundant check * Addres PR comment: Remove unused Dpi and add CmdArgs enum * Address PR comment: Make methods const and inline * Address PR comments: Expose GenerateUniqueId function and use const ref instead of passing wstring by value * Address PR comment: Use lamdba as callback * Address PR comment: Move GenerateUniqueId to ZoneWindowUtils namespace * Address PR comment: Use regular comparison instead of std::wstring::compare * Address PR comment: Use std::wstring_view for tmp file paths * Address PR comment: Use scoped lock when accessing member data * Address PR comment: Remove typedefs to increase code readability * Address PR comment: removed nullptr checks with corresponding tests * Address PR comment: Move ZoneSet object instead of copying * Address PR comment: Make FancyZonesData instance const where possible * Remove unnecessary gutter variable during calculating zone coordinates * Remove uneeded subclass * Avoid unnecessary copying and reserve space for vector if possible * Save FancyZones data after exiting editor * App zone history (#18) * added window and zone set ids to app zone history * Rename JSON file * Remove AppZoneHistory migration * Move parsing of ZoneWindow independent temp files outside of it * Unit tests update (#19) * check device existence in map * updated ZoneSet tests * updated JsonHelpers tests * Use single zone count information * Remove uneeded tests * Remove one more test * Remove uneeded line * Address PR comments - Missing whitespace * Update zoneset data for new virtual desktops (#21) * update active zone set with actual data * Introduce Blank zone set (used to indicate that no layout applied yet). Move parsing completely outside of ZoneWindow. * Fix unit tests to match modifications in implementation * Fix applying layouts on startup (second monitor) Co-authored-by: vldmr11080 <57061786+vldmr11080@users.noreply.github.com> Co-authored-by: Seraphima <zykovas91@gmail.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
namespace PTSettingsHelper {
|
namespace PTSettingsHelper {
|
||||||
|
|
||||||
|
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
|
||||||
|
|
||||||
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings);
|
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings);
|
||||||
json::JsonObject load_module_settings(std::wstring_view powertoy_name);
|
json::JsonObject load_module_settings(std::wstring_view powertoy_name);
|
||||||
void save_general_settings(const json::JsonObject& settings);
|
void save_general_settings(const json::JsonObject& settings);
|
||||||
|
|||||||
@@ -71,7 +71,6 @@
|
|||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
<AdditionalDependencies>gdiplus.lib;dwmapi.lib;shlwapi.lib;uxtheme.lib;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>gdiplus.lib;dwmapi.lib;shlwapi.lib;uxtheme.lib;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<ModuleDefinitionFile>fancyzones.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
@@ -96,7 +95,6 @@
|
|||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
<AdditionalDependencies>gdiplus.lib;dwmapi.lib;shlwapi.lib;uxtheme.lib;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>gdiplus.lib;dwmapi.lib;shlwapi.lib;uxtheme.lib;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<ModuleDefinitionFile>fancyzones.def</ModuleDefinitionFile>
|
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -121,7 +119,6 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="fancyzones.def" />
|
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="fancyzones.def" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\lib\fancyzones.rc" />
|
<ResourceCompile Include="..\lib\fancyzones.rc" />
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <interface/lowlevel_keyboard_event_data.h>
|
#include <interface/lowlevel_keyboard_event_data.h>
|
||||||
#include <interface/win_hook_event_data.h>
|
#include <interface/win_hook_event_data.h>
|
||||||
#include <lib/ZoneSet.h>
|
#include <lib/ZoneSet.h>
|
||||||
#include <lib/RegistryHelpers.h>
|
|
||||||
|
|
||||||
#include <lib/resource.h>
|
#include <lib/resource.h>
|
||||||
#include <lib/trace.h>
|
#include <lib/trace.h>
|
||||||
@@ -33,76 +32,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is exported and called from FancyZonesEditor.exe to save a layout from the editor.
|
|
||||||
STDAPI PersistZoneSet(
|
|
||||||
PCWSTR activeKey, // Registry key holding ActiveZoneSet
|
|
||||||
PCWSTR resolutionKey, // Registry key to persist ZoneSet to
|
|
||||||
HMONITOR monitor,
|
|
||||||
WORD layoutId, // LayoutModel Id
|
|
||||||
int zoneCount, // Number of zones in zones
|
|
||||||
int zones[]) // Array of zones serialized in left/top/right/bottom chunks
|
|
||||||
{
|
|
||||||
// See if we have already persisted this layout we can update.
|
|
||||||
UUID id{GUID_NULL};
|
|
||||||
if (wil::unique_hkey key{ RegistryHelpers::OpenKey(resolutionKey) })
|
|
||||||
{
|
|
||||||
ZoneSetPersistedData data{};
|
|
||||||
DWORD dataSize = sizeof(data);
|
|
||||||
wchar_t value[256]{};
|
|
||||||
DWORD valueLength = ARRAYSIZE(value);
|
|
||||||
DWORD i = 0;
|
|
||||||
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (data.LayoutId == layoutId)
|
|
||||||
{
|
|
||||||
if (data.ZoneCount == zoneCount)
|
|
||||||
{
|
|
||||||
CLSIDFromString(value, &id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valueLength = ARRAYSIZE(value);
|
|
||||||
dataSize = sizeof(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == GUID_NULL)
|
|
||||||
{
|
|
||||||
// No existing layout found so let's create a new one.
|
|
||||||
UuidCreate(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id != GUID_NULL)
|
|
||||||
{
|
|
||||||
winrt::com_ptr<IZoneSet> zoneSet = MakeZoneSet(
|
|
||||||
ZoneSetConfig(
|
|
||||||
id,
|
|
||||||
layoutId,
|
|
||||||
MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY),
|
|
||||||
resolutionKey));
|
|
||||||
|
|
||||||
for (int i = 0; i < zoneCount; i++)
|
|
||||||
{
|
|
||||||
const int baseIndex = i * 4;
|
|
||||||
const int left = zones[baseIndex];
|
|
||||||
const int top = zones[baseIndex+1];
|
|
||||||
const int right = zones[baseIndex+2];
|
|
||||||
const int bottom = zones[baseIndex+3];
|
|
||||||
zoneSet->AddZone(MakeZone({ left, top, right, bottom }));
|
|
||||||
}
|
|
||||||
zoneSet->Save();
|
|
||||||
|
|
||||||
wil::unique_cotaskmem_string zoneSetId;
|
|
||||||
if (SUCCEEDED_LOG(StringFromCLSID(id, &zoneSetId)))
|
|
||||||
{
|
|
||||||
RegistryHelpers::SetString(activeKey, L"ActiveZoneSetId", zoneSetId.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FancyZonesModule : public PowertoyModuleIface
|
class FancyZonesModule : public PowertoyModuleIface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -147,7 +76,7 @@ public:
|
|||||||
if (!m_app)
|
if (!m_app)
|
||||||
{
|
{
|
||||||
Trace::FancyZones::EnableFancyZones(true);
|
Trace::FancyZones::EnableFancyZones(true);
|
||||||
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings.get());
|
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings);
|
||||||
if (m_app)
|
if (m_app)
|
||||||
{
|
{
|
||||||
m_app->Run();
|
m_app->Run();
|
||||||
@@ -200,12 +129,15 @@ public:
|
|||||||
{
|
{
|
||||||
app_name = GET_RESOURCE_STRING(IDS_FANCYZONES);
|
app_name = GET_RESOURCE_STRING(IDS_FANCYZONES);
|
||||||
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name());
|
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name());
|
||||||
|
JSONHelpers::FancyZonesDataInstance().LoadFancyZonesData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Disable(bool const traceEvent)
|
void Disable(bool const traceEvent)
|
||||||
{
|
{
|
||||||
if (m_app) {
|
if (m_app) {
|
||||||
|
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||||
|
fancyZonesData.SaveFancyZonesData();
|
||||||
if (traceEvent)
|
if (traceEvent)
|
||||||
{
|
{
|
||||||
Trace::FancyZones::EnableFancyZones(false);
|
Trace::FancyZones::EnableFancyZones(false);
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
LIBRARY fancyzones.dll
|
|
||||||
|
|
||||||
EXPORTS
|
|
||||||
PersistZoneSet PRIVATE
|
|
||||||
@@ -15,8 +15,6 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
public Settings ZoneSettings { get; }
|
public Settings ZoneSettings { get; }
|
||||||
|
|
||||||
private ushort _idInitial = 0;
|
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
ZoneSettings = new Settings();
|
ZoneSettings = new Settings();
|
||||||
@@ -24,37 +22,29 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void OnStartup(object sender, StartupEventArgs e)
|
private void OnStartup(object sender, StartupEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Args.Length > 1)
|
|
||||||
{
|
|
||||||
ushort.TryParse(e.Args[1], out _idInitial);
|
|
||||||
}
|
|
||||||
|
|
||||||
LayoutModel foundModel = null;
|
LayoutModel foundModel = null;
|
||||||
|
|
||||||
if (_idInitial != 0)
|
foreach (LayoutModel model in ZoneSettings.DefaultModels)
|
||||||
{
|
{
|
||||||
foreach (LayoutModel model in ZoneSettings.DefaultModels)
|
if (model.Type == Settings.ActiveZoneSetLayoutType)
|
||||||
{
|
{
|
||||||
if (model.Id == _idInitial)
|
// found match
|
||||||
|
foundModel = model;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundModel == null)
|
||||||
|
{
|
||||||
|
foreach (LayoutModel model in Settings.CustomModels)
|
||||||
|
{
|
||||||
|
if ("{" + model.Guid.ToString().ToUpper() + "}" == Settings.ActiveZoneSetUUid.ToUpper())
|
||||||
{
|
{
|
||||||
// found match
|
// found match
|
||||||
foundModel = model;
|
foundModel = model;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundModel == null)
|
|
||||||
{
|
|
||||||
foreach (LayoutModel model in ZoneSettings.CustomModels)
|
|
||||||
{
|
|
||||||
if (model.Id == _idInitial)
|
|
||||||
{
|
|
||||||
// found match
|
|
||||||
foundModel = model;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundModel == null)
|
if (foundModel == null)
|
||||||
|
|||||||
@@ -18,8 +18,11 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
|
private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
|
||||||
private LayoutPreview _layoutPreview;
|
private LayoutPreview _layoutPreview;
|
||||||
|
|
||||||
private UserControl _editor;
|
private UserControl _editor;
|
||||||
|
|
||||||
|
private static MainWindow _mainWindow = new MainWindow();
|
||||||
|
|
||||||
public Int32Rect[] GetZoneRects()
|
public Int32Rect[] GetZoneRects()
|
||||||
{
|
{
|
||||||
if (_editor != null)
|
if (_editor != null)
|
||||||
@@ -79,27 +82,23 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
public void ShowLayoutPicker()
|
public void ShowLayoutPicker()
|
||||||
{
|
{
|
||||||
DataContext = null;
|
|
||||||
|
|
||||||
_editor = null;
|
_editor = null;
|
||||||
_layoutPreview = new LayoutPreview
|
_layoutPreview = new LayoutPreview
|
||||||
{
|
{
|
||||||
IsActualSize = true,
|
IsActualSize = true,
|
||||||
Opacity = 0.5,
|
Opacity = 0.5,
|
||||||
};
|
};
|
||||||
|
|
||||||
Content = _layoutPreview;
|
Content = _layoutPreview;
|
||||||
|
|
||||||
MainWindow window = new MainWindow
|
_mainWindow.Owner = this;
|
||||||
{
|
_mainWindow.ShowActivated = true;
|
||||||
Owner = this,
|
_mainWindow.Topmost = true;
|
||||||
ShowActivated = true,
|
_mainWindow.Show();
|
||||||
Topmost = true,
|
|
||||||
};
|
|
||||||
window.Show();
|
|
||||||
|
|
||||||
// window is set to topmost to make sure it shows on top of PowerToys settings page
|
// window is set to topmost to make sure it shows on top of PowerToys settings page
|
||||||
// we can reset topmost flag now
|
// we can reset topmost flag now
|
||||||
window.Topmost = false;
|
_mainWindow.Topmost = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard
|
// These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -194,6 +195,9 @@
|
|||||||
<PackageReference Include="MahApps.Metro">
|
<PackageReference Include="MahApps.Metro">
|
||||||
<Version>2.0.0-alpha0455</Version>
|
<Version>2.0.0-alpha0455</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Text.Json">
|
||||||
|
<Version>4.6.0</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="StyleCop.Analyzers">
|
<PackageReference Include="StyleCop.Analyzers">
|
||||||
<Version>1.1.118</Version>
|
<Version>1.1.118</Version>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@@ -208,4 +212,4 @@
|
|||||||
<Resource Include="images\Merge.png" />
|
<Resource Include="images\Merge.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace FancyZonesEditor
|
|||||||
public const int MaxZones = 40;
|
public const int MaxZones = 40;
|
||||||
private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
|
private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
|
||||||
private static readonly string _defaultNamePrefix = "Custom Layout ";
|
private static readonly string _defaultNamePrefix = "Custom Layout ";
|
||||||
private bool _editing = false;
|
|
||||||
|
|
||||||
public int WrapPanelItemSize { get; set; } = 262;
|
public int WrapPanelItemSize { get; set; } = 262;
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
WindowLayout window = new WindowLayout();
|
WindowLayout window = new WindowLayout();
|
||||||
window.Show();
|
window.Show();
|
||||||
Close();
|
Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
|
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
|
||||||
@@ -95,12 +94,11 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
model.IsSelected = false;
|
model.IsSelected = false;
|
||||||
_editing = true;
|
Hide();
|
||||||
Close();
|
|
||||||
|
|
||||||
bool isPredefinedLayout = Settings.IsPredefinedLayout(model);
|
bool isPredefinedLayout = Settings.IsPredefinedLayout(model);
|
||||||
|
|
||||||
if (!_settings.CustomModels.Contains(model) || isPredefinedLayout)
|
if (!Settings.CustomModels.Contains(model) || isPredefinedLayout)
|
||||||
{
|
{
|
||||||
if (isPredefinedLayout)
|
if (isPredefinedLayout)
|
||||||
{
|
{
|
||||||
@@ -110,7 +108,7 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
int maxCustomIndex = 0;
|
int maxCustomIndex = 0;
|
||||||
foreach (LayoutModel customModel in _settings.CustomModels)
|
foreach (LayoutModel customModel in Settings.CustomModels)
|
||||||
{
|
{
|
||||||
string name = customModel.Name;
|
string name = customModel.Name;
|
||||||
if (name.StartsWith(_defaultNamePrefix))
|
if (name.StartsWith(_defaultNamePrefix))
|
||||||
@@ -165,10 +163,8 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void OnClosing(object sender, EventArgs e)
|
private void OnClosing(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (!_editing)
|
LayoutModel.SerializeDeletedCustomZoneSets();
|
||||||
{
|
EditorOverlay.Current.Close();
|
||||||
EditorOverlay.Current.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInitialized(object sender, EventArgs e)
|
private void OnInitialized(object sender, EventArgs e)
|
||||||
@@ -178,7 +174,7 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void SetSelectedItem()
|
private void SetSelectedItem()
|
||||||
{
|
{
|
||||||
foreach (LayoutModel model in _settings.CustomModels)
|
foreach (LayoutModel model in Settings.CustomModels)
|
||||||
{
|
{
|
||||||
if (model.IsSelected)
|
if (model.IsSelected)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
namespace FancyZonesEditor.Models
|
namespace FancyZonesEditor.Models
|
||||||
@@ -11,40 +14,27 @@ namespace FancyZonesEditor.Models
|
|||||||
// Free form Layout Model, which specifies independent zone rects
|
// Free form Layout Model, which specifies independent zone rects
|
||||||
public class CanvasLayoutModel : LayoutModel
|
public class CanvasLayoutModel : LayoutModel
|
||||||
{
|
{
|
||||||
private static readonly ushort _latestVersion = 0;
|
public CanvasLayoutModel(string uuid, string name, LayoutType type, int referenceWidth, int referenceHeight, IList<Int32Rect> zones)
|
||||||
|
: base(uuid, name, type)
|
||||||
public CanvasLayoutModel(ushort version, string name, ushort id, byte[] data)
|
|
||||||
: base(name, id)
|
|
||||||
{
|
{
|
||||||
if (version == _latestVersion)
|
_referenceWidth = referenceWidth;
|
||||||
{
|
_referenceHeight = referenceHeight;
|
||||||
Load(data);
|
Zones = zones;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanvasLayoutModel(string name, ushort id, int referenceWidth, int referenceHeight)
|
public CanvasLayoutModel(string name, LayoutType type, int referenceWidth, int referenceHeight)
|
||||||
: base(name, id)
|
: base(name, type)
|
||||||
{
|
{
|
||||||
// Initialize Reference Size
|
// Initialize Reference Size
|
||||||
_referenceWidth = referenceWidth;
|
_referenceWidth = referenceWidth;
|
||||||
_referenceHeight = referenceHeight;
|
_referenceHeight = referenceHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanvasLayoutModel(string name, ushort id)
|
|
||||||
: base(name, id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public CanvasLayoutModel(string name)
|
public CanvasLayoutModel(string name)
|
||||||
: base(name)
|
: base(name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanvasLayoutModel()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReferenceWidth - the reference width for the layout rect that all Zones are relative to
|
// ReferenceWidth - the reference width for the layout rect that all Zones are relative to
|
||||||
public int ReferenceWidth
|
public int ReferenceWidth
|
||||||
{
|
{
|
||||||
@@ -104,26 +94,6 @@ namespace FancyZonesEditor.Models
|
|||||||
FirePropertyChanged("Zones");
|
FirePropertyChanged("Zones");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Load(byte[] data)
|
|
||||||
{
|
|
||||||
// Initialize this CanvasLayoutModel based on the given persistence data
|
|
||||||
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
|
|
||||||
int i = 5;
|
|
||||||
_referenceWidth = (data[i++] * 256) + data[i++];
|
|
||||||
_referenceHeight = (data[i++] * 256) + data[i++];
|
|
||||||
|
|
||||||
int count = data[i++];
|
|
||||||
|
|
||||||
while (count-- > 0)
|
|
||||||
{
|
|
||||||
Zones.Add(new Int32Rect(
|
|
||||||
(data[i++] * 256) + data[i++],
|
|
||||||
(data[i++] * 256) + data[i++],
|
|
||||||
(data[i++] * 256) + data[i++],
|
|
||||||
(data[i++] * 256) + data[i++]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone
|
// Clone
|
||||||
// Implements the LayoutModel.Clone abstract method
|
// Implements the LayoutModel.Clone abstract method
|
||||||
// Clones the data from this CanvasLayoutModel to a new CanvasLayoutModel
|
// Clones the data from this CanvasLayoutModel to a new CanvasLayoutModel
|
||||||
@@ -143,44 +113,50 @@ namespace FancyZonesEditor.Models
|
|||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPersistData
|
// PersistData
|
||||||
// Implements the LayoutModel.GetPersistData abstract method
|
// Implements the LayoutModel.PersistData abstract method
|
||||||
// Returns the state of this GridLayoutModel in persisted format
|
protected override void PersistData()
|
||||||
protected override byte[] GetPersistData()
|
|
||||||
{
|
{
|
||||||
byte[] data = new byte[10 + (Zones.Count * 8)];
|
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
|
||||||
int i = 0;
|
JsonWriterOptions writerOptions = new JsonWriterOptions
|
||||||
|
|
||||||
// Common persisted values between all layout types
|
|
||||||
data[i++] = (byte)(_latestVersion / 256);
|
|
||||||
data[i++] = (byte)(_latestVersion % 256);
|
|
||||||
data[i++] = 1; // LayoutModelType: 1 == CanvasLayoutModel
|
|
||||||
data[i++] = (byte)(Id / 256);
|
|
||||||
data[i++] = (byte)(Id % 256);
|
|
||||||
|
|
||||||
// End common
|
|
||||||
data[i++] = (byte)(_referenceWidth / 256);
|
|
||||||
data[i++] = (byte)(_referenceWidth % 256);
|
|
||||||
data[i++] = (byte)(_referenceHeight / 256);
|
|
||||||
data[i++] = (byte)(_referenceHeight % 256);
|
|
||||||
data[i++] = (byte)Zones.Count;
|
|
||||||
|
|
||||||
foreach (Int32Rect rect in Zones)
|
|
||||||
{
|
{
|
||||||
data[i++] = (byte)(rect.X / 256);
|
SkipValidation = true,
|
||||||
data[i++] = (byte)(rect.X % 256);
|
};
|
||||||
|
using (var writer = new Utf8JsonWriter(outputStream, writerOptions))
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||||
|
writer.WriteString("name", Name);
|
||||||
|
|
||||||
data[i++] = (byte)(rect.Y / 256);
|
writer.WriteString("type", "canvas");
|
||||||
data[i++] = (byte)(rect.Y % 256);
|
|
||||||
|
|
||||||
data[i++] = (byte)(rect.Width / 256);
|
writer.WriteStartObject("info");
|
||||||
data[i++] = (byte)(rect.Width % 256);
|
|
||||||
|
|
||||||
data[i++] = (byte)(rect.Height / 256);
|
writer.WriteNumber("ref-width", _referenceWidth);
|
||||||
data[i++] = (byte)(rect.Height % 256);
|
writer.WriteNumber("ref-height", _referenceHeight);
|
||||||
|
|
||||||
|
writer.WriteStartArray("zones");
|
||||||
|
foreach (Int32Rect rect in Zones)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteNumber("X", rect.X);
|
||||||
|
writer.WriteNumber("Y", rect.Y);
|
||||||
|
writer.WriteNumber("width", rect.Width);
|
||||||
|
writer.WriteNumber("height", rect.Height);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
|
||||||
|
// end info object
|
||||||
|
writer.WriteEndObject();
|
||||||
|
|
||||||
|
// end root object
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
outputStream.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,20 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
namespace FancyZonesEditor.Models
|
using System.Text;
|
||||||
{
|
using System.Text.Json;
|
||||||
// GridLayoutModel
|
|
||||||
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
|
namespace FancyZonesEditor.Models
|
||||||
public class GridLayoutModel : LayoutModel
|
{
|
||||||
{
|
// GridLayoutModel
|
||||||
private static readonly ushort _latestVersion = 0;
|
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
|
||||||
|
public class GridLayoutModel : LayoutModel
|
||||||
// Rows - number of rows in the Grid
|
{
|
||||||
public int Rows
|
// Rows - number of rows in the Grid
|
||||||
{
|
public int Rows
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _rows;
|
return _rows;
|
||||||
@@ -22,19 +23,19 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_rows != value)
|
if (_rows != value)
|
||||||
{
|
{
|
||||||
_rows = value;
|
_rows = value;
|
||||||
FirePropertyChanged("Rows");
|
FirePropertyChanged("Rows");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _rows = 1;
|
private int _rows = 1;
|
||||||
|
|
||||||
// Columns - number of columns in the Grid
|
// Columns - number of columns in the Grid
|
||||||
public int Columns
|
public int Columns
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _cols;
|
return _cols;
|
||||||
@@ -42,19 +43,19 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_cols != value)
|
if (_cols != value)
|
||||||
{
|
{
|
||||||
_cols = value;
|
_cols = value;
|
||||||
FirePropertyChanged("Columns");
|
FirePropertyChanged("Columns");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _cols = 1;
|
private int _cols = 1;
|
||||||
|
|
||||||
// CellChildMap - represents which "children" belong in which grid cells;
|
// CellChildMap - represents which "children" belong in which grid cells;
|
||||||
// shows spanning children by the same index appearing in adjacent cells
|
// shows spanning children by the same index appearing in adjacent cells
|
||||||
// TODO: ideally no setter here - this means moving logic like "split" over to model
|
// TODO: ideally no setter here - this means moving logic like "split" over to model
|
||||||
public int[,] CellChildMap { get; set; }
|
public int[,] CellChildMap { get; set; }
|
||||||
|
|
||||||
// RowPercents - represents the %age height of each row in the grid
|
// RowPercents - represents the %age height of each row in the grid
|
||||||
@@ -69,179 +70,159 @@ namespace FancyZonesEditor.Models
|
|||||||
public IList<int> FreeZones { get; } = new List<int>();
|
public IList<int> FreeZones { get; } = new List<int>();
|
||||||
|
|
||||||
public GridLayoutModel()
|
public GridLayoutModel()
|
||||||
: base()
|
: base()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridLayoutModel(string name)
|
public GridLayoutModel(string name)
|
||||||
: base(name)
|
: base(name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridLayoutModel(string name, ushort id)
|
public GridLayoutModel(string name, LayoutType type)
|
||||||
: base(name, id)
|
: base(name, type)
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public GridLayoutModel(ushort version, string name, ushort id, byte[] data)
|
|
||||||
: base(name, id)
|
|
||||||
{
|
|
||||||
if (version == _latestVersion)
|
|
||||||
{
|
|
||||||
Reload(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(byte[] data)
|
public GridLayoutModel(string uuid, string name, LayoutType type, int rows, int cols, int[] rowPercents, int[] colsPercents, int[,] cellChildMap)
|
||||||
{
|
: base(uuid, name, type)
|
||||||
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
|
{
|
||||||
int i = 5;
|
_rows = rows;
|
||||||
|
_cols = cols;
|
||||||
Rows = data[i++];
|
RowPercents = rowPercents;
|
||||||
Columns = data[i++];
|
ColumnPercents = colsPercents;
|
||||||
|
CellChildMap = cellChildMap;
|
||||||
RowPercents = new int[Rows];
|
}
|
||||||
for (int row = 0; row < Rows; row++)
|
|
||||||
{
|
|
||||||
RowPercents[row] = (data[i++] * 256) + data[i++];
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnPercents = new int[Columns];
|
|
||||||
for (int col = 0; col < Columns; col++)
|
|
||||||
{
|
|
||||||
ColumnPercents[col] = (data[i++] * 256) + data[i++];
|
|
||||||
}
|
|
||||||
|
|
||||||
CellChildMap = new int[Rows, Columns];
|
|
||||||
for (int row = 0; row < Rows; row++)
|
|
||||||
{
|
|
||||||
for (int col = 0; col < Columns; col++)
|
|
||||||
{
|
|
||||||
CellChildMap[row, col] = data[i++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone
|
|
||||||
// Implements the LayoutModel.Clone abstract method
|
|
||||||
// Clones the data from this GridLayoutModel to a new GridLayoutModel
|
|
||||||
public override LayoutModel Clone()
|
|
||||||
{
|
|
||||||
GridLayoutModel layout = new GridLayoutModel(Name);
|
|
||||||
int rows = Rows;
|
|
||||||
int cols = Columns;
|
|
||||||
|
|
||||||
layout.Rows = rows;
|
|
||||||
layout.Columns = cols;
|
|
||||||
|
|
||||||
int[,] cellChildMap = new int[rows, cols];
|
|
||||||
for (int row = 0; row < rows; row++)
|
|
||||||
{
|
|
||||||
for (int col = 0; col < cols; col++)
|
|
||||||
{
|
|
||||||
cellChildMap[row, col] = CellChildMap[row, col];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
layout.CellChildMap = cellChildMap;
|
|
||||||
|
|
||||||
int[] rowPercents = new int[rows];
|
|
||||||
for (int row = 0; row < rows; row++)
|
|
||||||
{
|
|
||||||
rowPercents[row] = RowPercents[row];
|
|
||||||
}
|
|
||||||
|
|
||||||
layout.RowPercents = rowPercents;
|
|
||||||
|
|
||||||
int[] colPercents = new int[cols];
|
|
||||||
for (int col = 0; col < cols; col++)
|
|
||||||
{
|
|
||||||
colPercents[col] = ColumnPercents[col];
|
|
||||||
}
|
|
||||||
|
|
||||||
layout.ColumnPercents = colPercents;
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPersistData
|
|
||||||
// Implements the LayoutModel.GetPersistData abstract method
|
|
||||||
// Returns the state of this GridLayoutModel in persisted format
|
|
||||||
protected override byte[] GetPersistData()
|
|
||||||
{
|
|
||||||
int rows = Rows;
|
|
||||||
int cols = Columns;
|
|
||||||
|
|
||||||
int[,] cellChildMap;
|
|
||||||
|
|
||||||
if (FreeZones.Count == 0)
|
|
||||||
{
|
|
||||||
// no unused indices -- so we can just use the _cellChildMap as is
|
|
||||||
cellChildMap = CellChildMap;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// compress cellChildMap to not have gaps for unused child indices;
|
|
||||||
List<int> mapping = new List<int>();
|
|
||||||
|
|
||||||
cellChildMap = new int[rows, cols];
|
|
||||||
|
|
||||||
for (int row = 0; row < rows; row++)
|
|
||||||
{
|
|
||||||
for (int col = 0; col < cols; col++)
|
|
||||||
{
|
|
||||||
int source = CellChildMap[row, col];
|
|
||||||
|
|
||||||
int index = mapping.IndexOf(source);
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
index = mapping.Count;
|
|
||||||
mapping.Add(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
cellChildMap[row, col] = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] data = new byte[7 + (Rows * 2) + (Columns * 2) + (Rows * Columns)];
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// Common persisted values between all layout types
|
public void Reload(byte[] data)
|
||||||
data[i++] = (byte)(_latestVersion / 256);
|
{
|
||||||
data[i++] = (byte)(_latestVersion % 256);
|
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
|
||||||
data[i++] = 0; // LayoutModelType: 0 == GridLayoutModel
|
int i = 5;
|
||||||
data[i++] = (byte)(Id / 256);
|
|
||||||
data[i++] = (byte)(Id % 256);
|
Rows = data[i++];
|
||||||
|
Columns = data[i++];
|
||||||
// End common
|
|
||||||
data[i++] = (byte)Rows;
|
RowPercents = new int[Rows];
|
||||||
data[i++] = (byte)Columns;
|
for (int row = 0; row < Rows; row++)
|
||||||
|
{
|
||||||
for (int row = 0; row < Rows; row++)
|
RowPercents[row] = (data[i++] * 256) + data[i++];
|
||||||
{
|
}
|
||||||
int rowPercent = RowPercents[row];
|
|
||||||
data[i++] = (byte)(rowPercent / 256);
|
ColumnPercents = new int[Columns];
|
||||||
data[i++] = (byte)(rowPercent % 256);
|
for (int col = 0; col < Columns; col++)
|
||||||
}
|
{
|
||||||
|
ColumnPercents[col] = (data[i++] * 256) + data[i++];
|
||||||
for (int col = 0; col < Columns; col++)
|
}
|
||||||
{
|
|
||||||
int colPercent = ColumnPercents[col];
|
CellChildMap = new int[Rows, Columns];
|
||||||
data[i++] = (byte)(colPercent / 256);
|
for (int row = 0; row < Rows; row++)
|
||||||
data[i++] = (byte)(colPercent % 256);
|
{
|
||||||
}
|
for (int col = 0; col < Columns; col++)
|
||||||
|
{
|
||||||
for (int row = 0; row < Rows; row++)
|
CellChildMap[row, col] = data[i++];
|
||||||
{
|
}
|
||||||
for (int col = 0; col < Columns; col++)
|
}
|
||||||
{
|
}
|
||||||
data[i++] = (byte)cellChildMap[row, col];
|
|
||||||
}
|
// Clone
|
||||||
}
|
// Implements the LayoutModel.Clone abstract method
|
||||||
|
// Clones the data from this GridLayoutModel to a new GridLayoutModel
|
||||||
return data;
|
public override LayoutModel Clone()
|
||||||
}
|
{
|
||||||
}
|
GridLayoutModel layout = new GridLayoutModel(Name);
|
||||||
}
|
int rows = Rows;
|
||||||
|
int cols = Columns;
|
||||||
|
|
||||||
|
layout.Rows = rows;
|
||||||
|
layout.Columns = cols;
|
||||||
|
|
||||||
|
int[,] cellChildMap = new int[rows, cols];
|
||||||
|
for (int row = 0; row < rows; row++)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < cols; col++)
|
||||||
|
{
|
||||||
|
cellChildMap[row, col] = CellChildMap[row, col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.CellChildMap = cellChildMap;
|
||||||
|
|
||||||
|
int[] rowPercents = new int[rows];
|
||||||
|
for (int row = 0; row < rows; row++)
|
||||||
|
{
|
||||||
|
rowPercents[row] = RowPercents[row];
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.RowPercents = rowPercents;
|
||||||
|
|
||||||
|
int[] colPercents = new int[cols];
|
||||||
|
for (int col = 0; col < cols; col++)
|
||||||
|
{
|
||||||
|
colPercents[col] = ColumnPercents[col];
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.ColumnPercents = colPercents;
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistData
|
||||||
|
// Implements the LayoutModel.PersistData abstract method
|
||||||
|
protected override void PersistData()
|
||||||
|
{
|
||||||
|
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
|
||||||
|
using (var writer = new Utf8JsonWriter(outputStream, options: default))
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||||
|
writer.WriteString("name", Name);
|
||||||
|
|
||||||
|
writer.WriteString("type", "grid");
|
||||||
|
|
||||||
|
writer.WriteStartObject("info");
|
||||||
|
|
||||||
|
writer.WriteNumber("rows", Rows);
|
||||||
|
writer.WriteNumber("columns", Columns);
|
||||||
|
|
||||||
|
writer.WriteStartArray("rows-percentage");
|
||||||
|
for (int row = 0; row < Rows; row++)
|
||||||
|
{
|
||||||
|
writer.WriteNumberValue(RowPercents[row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
|
||||||
|
writer.WriteStartArray("columns-percentage");
|
||||||
|
for (int col = 0; col < Columns; col++)
|
||||||
|
{
|
||||||
|
writer.WriteNumberValue(ColumnPercents[col]);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
|
||||||
|
writer.WriteStartArray("cell-child-map");
|
||||||
|
for (int row = 0; row < Rows; row++)
|
||||||
|
{
|
||||||
|
writer.WriteStartArray();
|
||||||
|
for (int col = 0; col < Columns; col++)
|
||||||
|
{
|
||||||
|
writer.WriteNumberValue(CellChildMap[row, col]);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
|
||||||
|
// end info object
|
||||||
|
writer.WriteEndObject();
|
||||||
|
|
||||||
|
// end root object
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.InteropServices;
|
using System.IO;
|
||||||
using Microsoft.Win32;
|
using System.Text.Json;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace FancyZonesEditor.Models
|
namespace FancyZonesEditor.Models
|
||||||
{
|
{
|
||||||
|
public enum LayoutType
|
||||||
|
{
|
||||||
|
Blank = -1,
|
||||||
|
Focus,
|
||||||
|
Columns,
|
||||||
|
Rows,
|
||||||
|
Grid,
|
||||||
|
PriorityGrid,
|
||||||
|
Custom,
|
||||||
|
}
|
||||||
|
|
||||||
// Base LayoutModel
|
// Base LayoutModel
|
||||||
// Manages common properties and base persistence
|
// Manages common properties and base persistence
|
||||||
public abstract class LayoutModel : INotifyPropertyChanged
|
public abstract class LayoutModel : INotifyPropertyChanged
|
||||||
@@ -19,18 +32,30 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
protected LayoutModel()
|
protected LayoutModel()
|
||||||
{
|
{
|
||||||
|
_guid = Guid.NewGuid();
|
||||||
|
Type = LayoutType.Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LayoutModel(string name)
|
protected LayoutModel(string name)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
|
_guid = Guid.NewGuid();
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LayoutModel(string name, ushort id)
|
protected LayoutModel(string uuid, string name, LayoutType type)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
_guid = Guid.Parse(uuid);
|
||||||
|
Name = name;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LayoutModel(string name, LayoutType type)
|
||||||
: this(name)
|
: this(name)
|
||||||
{
|
{
|
||||||
_id = id;
|
_guid = Guid.NewGuid();
|
||||||
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name - the display name for this layout model - is also used as the key in the registry
|
// Name - the display name for this layout model - is also used as the key in the registry
|
||||||
@@ -53,22 +78,17 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
private string _name;
|
private string _name;
|
||||||
|
|
||||||
// Id - the unique ID for this layout model - is used to connect fancy zones' ZonesSets with the editor's Layouts
|
public LayoutType Type { get; set; }
|
||||||
// - note: 0 means this is a new layout, which means it will have its ID auto-assigned on persist
|
|
||||||
public ushort Id
|
public Guid Guid
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_id == 0)
|
return _guid;
|
||||||
{
|
|
||||||
_id = ++_maxId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ushort _id = 0;
|
private Guid _guid;
|
||||||
|
|
||||||
// IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker
|
// IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker
|
||||||
// TODO: once we switch to a picker per monitor, we need to move this state to the view
|
// TODO: once we switch to a picker per monitor, we need to move this state to the view
|
||||||
@@ -103,51 +123,99 @@ namespace FancyZonesEditor.Models
|
|||||||
// Removes this Layout from the registry and the loaded CustomModels list
|
// Removes this Layout from the registry and the loaded CustomModels list
|
||||||
public void Delete()
|
public void Delete()
|
||||||
{
|
{
|
||||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryPath, true);
|
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
key.DeleteValue(Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = _customModels.IndexOf(this);
|
int i = _customModels.IndexOf(this);
|
||||||
if (i != -1)
|
if (i != -1)
|
||||||
{
|
{
|
||||||
_customModels.RemoveAt(i);
|
_customModels.RemoveAt(i);
|
||||||
|
_deletedCustomModels.Add(Guid.ToString().ToUpper());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads all the Layouts persisted under the Layouts key in the registry
|
public static void SerializeDeletedCustomZoneSets()
|
||||||
|
{
|
||||||
|
FileStream outputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Create);
|
||||||
|
var writer = new Utf8JsonWriter(outputStream, options: default);
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteStartArray("deleted-custom-zone-sets");
|
||||||
|
foreach (string zoneSet in _deletedCustomModels)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(zoneSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Flush();
|
||||||
|
outputStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads all the custom Layouts from tmp file passed by FancuZonesLib
|
||||||
public static ObservableCollection<LayoutModel> LoadCustomModels()
|
public static ObservableCollection<LayoutModel> LoadCustomModels()
|
||||||
{
|
{
|
||||||
_customModels = new ObservableCollection<LayoutModel>();
|
_customModels = new ObservableCollection<LayoutModel>();
|
||||||
|
|
||||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryPath);
|
FileStream inputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Open);
|
||||||
if (key != null)
|
var jsonObject = JsonDocument.Parse(inputStream, options: default);
|
||||||
|
JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty("custom-zone-sets").EnumerateArray();
|
||||||
|
while (customZoneSetsEnumerator.MoveNext())
|
||||||
{
|
{
|
||||||
foreach (string name in key.GetValueNames())
|
var current = customZoneSetsEnumerator.Current;
|
||||||
|
string name = current.GetProperty("name").GetString();
|
||||||
|
string type = current.GetProperty("type").GetString();
|
||||||
|
string uuid = current.GetProperty("uuid").GetString();
|
||||||
|
var info = current.GetProperty("info");
|
||||||
|
if (type.Equals("grid"))
|
||||||
{
|
{
|
||||||
LayoutModel model = null;
|
int rows = info.GetProperty("rows").GetInt32();
|
||||||
byte[] data = (byte[])Registry.GetValue(_fullRegistryPath, name, null);
|
int columns = info.GetProperty("columns").GetInt32();
|
||||||
|
int[] rowsPercentage = new int[rows];
|
||||||
ushort version = (ushort)((data[0] * 256) + data[1]);
|
JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty("rows-percentage").EnumerateArray();
|
||||||
byte type = data[2];
|
int i = 0;
|
||||||
ushort id = (ushort)((data[3] * 256) + data[4]);
|
while (rowsPercentageEnumerator.MoveNext())
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
{
|
||||||
case 0: model = new GridLayoutModel(version, name, id, data); break;
|
rowsPercentage[i++] = rowsPercentageEnumerator.Current.GetInt32();
|
||||||
case 1: model = new CanvasLayoutModel(version, name, id, data); break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model != null)
|
i = 0;
|
||||||
|
int[] columnsPercentage = new int[columns];
|
||||||
|
JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty("columns-percentage").EnumerateArray();
|
||||||
|
while (columnsPercentageEnumerator.MoveNext())
|
||||||
{
|
{
|
||||||
if (_maxId < id)
|
columnsPercentage[i++] = columnsPercentageEnumerator.Current.GetInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty("cell-child-map").EnumerateArray();
|
||||||
|
int[,] cellChildMap = new int[rows, columns];
|
||||||
|
while (cellChildMapRows.MoveNext())
|
||||||
|
{
|
||||||
|
int j = 0;
|
||||||
|
JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray();
|
||||||
|
while (cellChildMapRowElems.MoveNext())
|
||||||
{
|
{
|
||||||
_maxId = id;
|
cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
_customModels.Add(model);
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_customModels.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap));
|
||||||
|
}
|
||||||
|
else if (type.Equals("canvas"))
|
||||||
|
{
|
||||||
|
int referenceWidth = info.GetProperty("ref-width").GetInt32();
|
||||||
|
int referenceHeight = info.GetProperty("ref-height").GetInt32();
|
||||||
|
JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty("zones").EnumerateArray();
|
||||||
|
IList<Int32Rect> zones = new List<Int32Rect>();
|
||||||
|
while (zonesEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
int x = zonesEnumerator.Current.GetProperty("X").GetInt32();
|
||||||
|
int y = zonesEnumerator.Current.GetProperty("Y").GetInt32();
|
||||||
|
int width = zonesEnumerator.Current.GetProperty("width").GetInt32();
|
||||||
|
int height = zonesEnumerator.Current.GetProperty("height").GetInt32();
|
||||||
|
zones.Add(new Int32Rect(x, y, width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
_customModels.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, referenceWidth, referenceHeight, zones));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,55 +223,63 @@ namespace FancyZonesEditor.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ObservableCollection<LayoutModel> _customModels = null;
|
private static ObservableCollection<LayoutModel> _customModels = null;
|
||||||
|
private static List<string> _deletedCustomModels = new List<string>();
|
||||||
private static ushort _maxId = 0;
|
|
||||||
|
|
||||||
// Callbacks that the base LayoutModel makes to derived types
|
// Callbacks that the base LayoutModel makes to derived types
|
||||||
protected abstract byte[] GetPersistData();
|
protected abstract void PersistData();
|
||||||
|
|
||||||
public abstract LayoutModel Clone();
|
public abstract LayoutModel Clone();
|
||||||
|
|
||||||
public void Persist(System.Windows.Int32Rect[] zones)
|
public void Persist(System.Windows.Int32Rect[] zones)
|
||||||
{
|
{
|
||||||
// Persist the editor data
|
PersistData();
|
||||||
Registry.SetValue(_fullRegistryPath, Name, GetPersistData(), Microsoft.Win32.RegistryValueKind.Binary);
|
|
||||||
Apply(zones);
|
Apply(zones);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(System.Windows.Int32Rect[] zones)
|
public void Apply(System.Windows.Int32Rect[] zones)
|
||||||
{
|
{
|
||||||
// Persist the zone data back into FZ
|
|
||||||
var module = Native.LoadLibrary("fancyzones.dll");
|
|
||||||
if (module == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pfn = Native.GetProcAddress(module, "PersistZoneSet");
|
|
||||||
if (pfn == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale all the zones to the DPI and then pack them up to be marshalled.
|
|
||||||
int zoneCount = zones.Length;
|
int zoneCount = zones.Length;
|
||||||
var zoneArray = new int[zoneCount * 4];
|
FileStream outputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Create);
|
||||||
for (int i = 0; i < zones.Length; i++)
|
var writer = new Utf8JsonWriter(outputStream, options: default);
|
||||||
{
|
|
||||||
var left = (int)(zones[i].X * Settings.Dpi);
|
|
||||||
var top = (int)(zones[i].Y * Settings.Dpi);
|
|
||||||
var right = left + (int)(zones[i].Width * Settings.Dpi);
|
|
||||||
var bottom = top + (int)(zones[i].Height * Settings.Dpi);
|
|
||||||
|
|
||||||
var index = i * 4;
|
writer.WriteStartObject();
|
||||||
zoneArray[index] = left;
|
writer.WriteString("device-id", Settings.UniqueKey);
|
||||||
zoneArray[index + 1] = top;
|
|
||||||
zoneArray[index + 2] = right;
|
writer.WriteStartObject("active-zoneset");
|
||||||
zoneArray[index + 3] = bottom;
|
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case LayoutType.Focus:
|
||||||
|
writer.WriteString("type", "focus");
|
||||||
|
break;
|
||||||
|
case LayoutType.Rows:
|
||||||
|
writer.WriteString("type", "rows");
|
||||||
|
break;
|
||||||
|
case LayoutType.Columns:
|
||||||
|
writer.WriteString("type", "columns");
|
||||||
|
break;
|
||||||
|
case LayoutType.Grid:
|
||||||
|
writer.WriteString("type", "grid");
|
||||||
|
break;
|
||||||
|
case LayoutType.PriorityGrid:
|
||||||
|
writer.WriteString("type", "priority-grid");
|
||||||
|
break;
|
||||||
|
case LayoutType.Custom:
|
||||||
|
writer.WriteString("type", "custom");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var persistZoneSet = Marshal.GetDelegateForFunctionPointer<Native.PersistZoneSet>(pfn);
|
writer.WriteEndObject();
|
||||||
persistZoneSet(Settings.UniqueKey, Settings.WorkAreaKey, Settings.Monitor, _id, zoneCount, zoneArray);
|
|
||||||
|
Settings settings = ((App)Application.Current).ZoneSettings;
|
||||||
|
|
||||||
|
writer.WriteBoolean("editor-show-spacing", settings.ShowSpacing);
|
||||||
|
writer.WriteNumber("editor-spacing", settings.Spacing);
|
||||||
|
writer.WriteNumber("editor-zone-count", settings.ZoneCount);
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Flush();
|
||||||
|
outputStream.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
{
|
{
|
||||||
@@ -18,20 +19,30 @@ namespace FancyZonesEditor
|
|||||||
// Other UIs in the editor will subscribe to change events on the properties to stay up to date as these properties change
|
// Other UIs in the editor will subscribe to change events on the properties to stay up to date as these properties change
|
||||||
public class Settings : INotifyPropertyChanged
|
public class Settings : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private readonly CanvasLayoutModel _blankCustomModel;
|
private enum CmdArgs
|
||||||
|
{
|
||||||
|
MonitorHandle = 1,
|
||||||
|
X_Y_Width_Height,
|
||||||
|
ResolutionKey,
|
||||||
|
ActiveZoneSetTmpFile,
|
||||||
|
AppliedZoneSetTmpFile,
|
||||||
|
CustomZoneSetsTmpFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CanvasLayoutModel _blankCustomModel;
|
||||||
private readonly CanvasLayoutModel _focusModel;
|
private readonly CanvasLayoutModel _focusModel;
|
||||||
private readonly GridLayoutModel _rowsModel;
|
private readonly GridLayoutModel _rowsModel;
|
||||||
private readonly GridLayoutModel _columnsModel;
|
private readonly GridLayoutModel _columnsModel;
|
||||||
private readonly GridLayoutModel _gridModel;
|
private readonly GridLayoutModel _gridModel;
|
||||||
private readonly GridLayoutModel _priorityGridModel;
|
private readonly GridLayoutModel _priorityGridModel;
|
||||||
|
|
||||||
private static readonly ushort _focusModelId = 0xFFFF;
|
public const ushort _focusModelId = 0xFFFF;
|
||||||
private static readonly ushort _rowsModelId = 0xFFFE;
|
public const ushort _rowsModelId = 0xFFFE;
|
||||||
private static readonly ushort _columnsModelId = 0xFFFD;
|
public const ushort _columnsModelId = 0xFFFD;
|
||||||
private static readonly ushort _gridModelId = 0xFFFC;
|
public const ushort _gridModelId = 0xFFFC;
|
||||||
private static readonly ushort _priorityGridModelId = 0xFFFB;
|
public const ushort _priorityGridModelId = 0xFFFB;
|
||||||
private static readonly ushort _blankCustomModelId = 0xFFFA;
|
public const ushort _blankCustomModelId = 0xFFFA;
|
||||||
private static readonly ushort _lastPrefinedId = _blankCustomModelId;
|
public const ushort _lastPrefinedId = _blankCustomModelId;
|
||||||
|
|
||||||
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
|
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
|
||||||
private static readonly byte[][] _priorityData = new byte[][]
|
private static readonly byte[][] _priorityData = new byte[][]
|
||||||
@@ -73,34 +84,30 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
|
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
|
||||||
DefaultModels = new List<LayoutModel>(5);
|
DefaultModels = new List<LayoutModel>(5);
|
||||||
_focusModel = new CanvasLayoutModel("Focus", _focusModelId, (int)_workArea.Width, (int)_workArea.Height);
|
_focusModel = new CanvasLayoutModel("Focus", LayoutType.Focus, (int)_workArea.Width, (int)_workArea.Height);
|
||||||
DefaultModels.Add(_focusModel);
|
DefaultModels.Add(_focusModel);
|
||||||
|
|
||||||
_columnsModel = new GridLayoutModel("Columns", _columnsModelId)
|
_columnsModel = new GridLayoutModel("Columns", LayoutType.Columns)
|
||||||
{
|
{
|
||||||
Rows = 1,
|
Rows = 1,
|
||||||
RowPercents = new int[1] { _multiplier },
|
RowPercents = new int[1] { _multiplier },
|
||||||
};
|
};
|
||||||
DefaultModels.Add(_columnsModel);
|
DefaultModels.Add(_columnsModel);
|
||||||
|
|
||||||
_rowsModel = new GridLayoutModel("Rows", _rowsModelId)
|
_rowsModel = new GridLayoutModel("Rows", LayoutType.Rows)
|
||||||
{
|
{
|
||||||
Columns = 1,
|
Columns = 1,
|
||||||
ColumnPercents = new int[1] { _multiplier },
|
ColumnPercents = new int[1] { _multiplier },
|
||||||
};
|
};
|
||||||
DefaultModels.Add(_rowsModel);
|
DefaultModels.Add(_rowsModel);
|
||||||
|
|
||||||
_gridModel = new GridLayoutModel("Grid", _gridModelId);
|
_gridModel = new GridLayoutModel("Grid", LayoutType.Grid);
|
||||||
DefaultModels.Add(_gridModel);
|
DefaultModels.Add(_gridModel);
|
||||||
|
|
||||||
_priorityGridModel = new GridLayoutModel("Priority Grid", _priorityGridModelId);
|
_priorityGridModel = new GridLayoutModel("Priority Grid", LayoutType.PriorityGrid);
|
||||||
DefaultModels.Add(_priorityGridModel);
|
DefaultModels.Add(_priorityGridModel);
|
||||||
|
|
||||||
_blankCustomModel = new CanvasLayoutModel("Create new custom", _blankCustomModelId, (int)_workArea.Width, (int)_workArea.Height);
|
_blankCustomModel = new CanvasLayoutModel("Create new custom", LayoutType.Blank, (int)_workArea.Width, (int)_workArea.Height);
|
||||||
|
|
||||||
_zoneCount = ReadRegistryInt("ZoneCount", 3);
|
|
||||||
_spacing = ReadRegistryInt("Spacing", 16);
|
|
||||||
_showSpacing = ReadRegistryInt("ShowSpacing", 1) == 1;
|
|
||||||
|
|
||||||
UpdateLayoutModels();
|
UpdateLayoutModels();
|
||||||
}
|
}
|
||||||
@@ -118,7 +125,6 @@ namespace FancyZonesEditor
|
|||||||
if (_zoneCount != value)
|
if (_zoneCount != value)
|
||||||
{
|
{
|
||||||
_zoneCount = value;
|
_zoneCount = value;
|
||||||
Registry.SetValue(_uniqueRegistryPath, "ZoneCount", _zoneCount, RegistryValueKind.DWord);
|
|
||||||
UpdateLayoutModels();
|
UpdateLayoutModels();
|
||||||
FirePropertyChanged("ZoneCount");
|
FirePropertyChanged("ZoneCount");
|
||||||
}
|
}
|
||||||
@@ -140,7 +146,6 @@ namespace FancyZonesEditor
|
|||||||
if (_spacing != value)
|
if (_spacing != value)
|
||||||
{
|
{
|
||||||
_spacing = value;
|
_spacing = value;
|
||||||
Registry.SetValue(_uniqueRegistryPath, "Spacing", _spacing, RegistryValueKind.DWord);
|
|
||||||
FirePropertyChanged("Spacing");
|
FirePropertyChanged("Spacing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +166,6 @@ namespace FancyZonesEditor
|
|||||||
if (_showSpacing != value)
|
if (_showSpacing != value)
|
||||||
{
|
{
|
||||||
_showSpacing = value;
|
_showSpacing = value;
|
||||||
Registry.SetValue(_uniqueRegistryPath, "ShowSpacing", _showSpacing, RegistryValueKind.DWord);
|
|
||||||
FirePropertyChanged("ShowSpacing");
|
FirePropertyChanged("ShowSpacing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,18 +224,33 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
public static string UniqueKey { get; private set; }
|
public static string UniqueKey { get; private set; }
|
||||||
|
|
||||||
private string _uniqueRegistryPath;
|
public static string ActiveZoneSetUUid { get; private set; }
|
||||||
|
|
||||||
|
public static LayoutType ActiveZoneSetLayoutType { get; private set; }
|
||||||
|
|
||||||
|
public static string ActiveZoneSetTmpFile
|
||||||
|
{
|
||||||
|
get { return _activeZoneSetTmpFile; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string _activeZoneSetTmpFile;
|
||||||
|
|
||||||
|
public static string AppliedZoneSetTmpFile
|
||||||
|
{
|
||||||
|
get { return _appliedZoneSetTmpFile; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string _appliedZoneSetTmpFile;
|
||||||
|
|
||||||
|
public static string CustomZoneSetsTmpFile
|
||||||
|
{
|
||||||
|
get { return _customZoneSetsTmpFile; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string _customZoneSetsTmpFile;
|
||||||
|
|
||||||
public static string WorkAreaKey { get; private set; }
|
public static string WorkAreaKey { get; private set; }
|
||||||
|
|
||||||
public static float Dpi { get; private set; }
|
|
||||||
|
|
||||||
private int ReadRegistryInt(string valueName, int defaultValue)
|
|
||||||
{
|
|
||||||
object obj = Registry.GetValue(_uniqueRegistryPath, valueName, defaultValue);
|
|
||||||
return (obj != null) ? (int)obj : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLayoutModels
|
// UpdateLayoutModels
|
||||||
// Update the five default layouts based on the new ZoneCount
|
// Update the five default layouts based on the new ZoneCount
|
||||||
private void UpdateLayoutModels()
|
private void UpdateLayoutModels()
|
||||||
@@ -327,59 +346,87 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ParseDeviceInfoData()
|
||||||
|
{
|
||||||
|
FileStream inputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open);
|
||||||
|
var jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
|
||||||
|
|
||||||
|
UniqueKey = jsonObject.GetProperty("device-id").GetString();
|
||||||
|
ActiveZoneSetUUid = jsonObject.GetProperty("active-zoneset").GetProperty("uuid").GetString();
|
||||||
|
string layoutType = jsonObject.GetProperty("active-zoneset").GetProperty("type").GetString();
|
||||||
|
|
||||||
|
if (ActiveZoneSetUUid == "null" || layoutType == "blank")
|
||||||
|
{
|
||||||
|
// Default selection is Focus
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Focus;
|
||||||
|
_showSpacing = true;
|
||||||
|
_spacing = 16;
|
||||||
|
_zoneCount = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (layoutType)
|
||||||
|
{
|
||||||
|
case "focus":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Focus;
|
||||||
|
break;
|
||||||
|
case "columns":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Columns;
|
||||||
|
break;
|
||||||
|
case "rows":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Rows;
|
||||||
|
break;
|
||||||
|
case "grid":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Grid;
|
||||||
|
break;
|
||||||
|
case "priority-grid":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.PriorityGrid;
|
||||||
|
break;
|
||||||
|
case "custom":
|
||||||
|
ActiveZoneSetLayoutType = LayoutType.Custom;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_showSpacing = jsonObject.GetProperty("editor-show-spacing").GetBoolean();
|
||||||
|
_spacing = jsonObject.GetProperty("editor-spacing").GetInt32();
|
||||||
|
_zoneCount = jsonObject.GetProperty("editor-zone-count").GetInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ParseCommandLineArgs()
|
private void ParseCommandLineArgs()
|
||||||
{
|
{
|
||||||
_workArea = SystemParameters.WorkArea;
|
_workArea = SystemParameters.WorkArea;
|
||||||
Monitor = 0;
|
Monitor = 0;
|
||||||
_uniqueRegistryPath = FullRegistryPath;
|
|
||||||
UniqueKey = string.Empty;
|
|
||||||
Dpi = 1;
|
|
||||||
|
|
||||||
string[] args = Environment.GetCommandLineArgs();
|
string[] args = Environment.GetCommandLineArgs();
|
||||||
if (args.Length == 7)
|
if (args.Length == 7)
|
||||||
{
|
{
|
||||||
// 1 = unique key for per-monitor settings
|
if (uint.TryParse(args[(int)CmdArgs.MonitorHandle], out uint monitor))
|
||||||
// 2 = layoutid used to generate current layout (used to pick the default layout to show)
|
{
|
||||||
// 3 = handle to monitor (passed back to engine to persist data)
|
Monitor = monitor;
|
||||||
// 4 = X_Y_Width_Height in a dpi-scaled-but-unaware coords (where EditorOverlay shows up)
|
}
|
||||||
// 5 = resolution key (passed back to engine to persist data)
|
|
||||||
// 6 = monitor DPI (float)
|
|
||||||
UniqueKey = args[1];
|
|
||||||
_uniqueRegistryPath += "\\" + UniqueKey;
|
|
||||||
|
|
||||||
var parsedLocation = args[4].Split('_');
|
var parsedLocation = args[(int)CmdArgs.X_Y_Width_Height].Split('_');
|
||||||
var x = int.Parse(parsedLocation[0]);
|
var x = int.Parse(parsedLocation[0]);
|
||||||
var y = int.Parse(parsedLocation[1]);
|
var y = int.Parse(parsedLocation[1]);
|
||||||
var width = int.Parse(parsedLocation[2]);
|
var width = int.Parse(parsedLocation[2]);
|
||||||
var height = int.Parse(parsedLocation[3]);
|
var height = int.Parse(parsedLocation[3]);
|
||||||
|
|
||||||
WorkAreaKey = args[5];
|
|
||||||
|
|
||||||
// Try invariant culture first, caller likely uses invariant i.e. "C" locale to construct parameters
|
|
||||||
foreach (var cultureInfo in new[] { CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture })
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Dpi = float.Parse(args[6], cultureInfo);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_workArea = new Rect(x, y, width, height);
|
_workArea = new Rect(x, y, width, height);
|
||||||
|
|
||||||
if (uint.TryParse(args[4], out uint monitor))
|
WorkAreaKey = args[(int)CmdArgs.ResolutionKey];
|
||||||
{
|
|
||||||
Monitor = monitor;
|
_activeZoneSetTmpFile = args[(int)CmdArgs.ActiveZoneSetTmpFile];
|
||||||
}
|
_appliedZoneSetTmpFile = args[(int)CmdArgs.AppliedZoneSetTmpFile];
|
||||||
|
_customZoneSetsTmpFile = args[(int)CmdArgs.CustomZoneSetsTmpFile];
|
||||||
|
|
||||||
|
ParseDeviceInfoData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<LayoutModel> DefaultModels { get; }
|
public IList<LayoutModel> DefaultModels { get; }
|
||||||
|
|
||||||
public ObservableCollection<LayoutModel> CustomModels
|
public static ObservableCollection<LayoutModel> CustomModels
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -393,14 +440,14 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObservableCollection<LayoutModel> _customModels;
|
private static ObservableCollection<LayoutModel> _customModels;
|
||||||
|
|
||||||
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
|
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
|
||||||
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
|
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
|
||||||
|
|
||||||
public static bool IsPredefinedLayout(LayoutModel model)
|
public static bool IsPredefinedLayout(LayoutModel model)
|
||||||
{
|
{
|
||||||
return model.Id >= _lastPrefinedId;
|
return model.Type != LayoutType.Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementation of INotifyProeprtyChanged
|
// implementation of INotifyProeprtyChanged
|
||||||
|
|||||||
@@ -6,14 +6,27 @@
|
|||||||
#include "lib/Settings.h"
|
#include "lib/Settings.h"
|
||||||
#include "lib/ZoneWindow.h"
|
#include "lib/ZoneWindow.h"
|
||||||
#include "lib/RegistryHelpers.h"
|
#include "lib/RegistryHelpers.h"
|
||||||
|
#include "lib/JsonHelpers.h"
|
||||||
|
#include "lib/ZoneSet.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <common/common.h>
|
#include <common/common.h>
|
||||||
|
#include <lib\util.h>
|
||||||
|
|
||||||
|
enum class DisplayChangeType
|
||||||
|
{
|
||||||
|
WorkArea,
|
||||||
|
DisplayChange,
|
||||||
|
VirtualDesktop,
|
||||||
|
Editor,
|
||||||
|
Initialization
|
||||||
|
};
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
template<> struct hash<GUID>
|
template<>
|
||||||
|
struct hash<GUID>
|
||||||
{
|
{
|
||||||
size_t operator()(const GUID& Value) const
|
size_t operator()(const GUID& Value) const
|
||||||
{
|
{
|
||||||
@@ -26,9 +39,9 @@ namespace std
|
|||||||
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
|
FancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept :
|
||||||
: m_hinstance(hinstance)
|
m_hinstance(hinstance),
|
||||||
, m_settings(settings)
|
m_settings(settings)
|
||||||
{
|
{
|
||||||
m_settings->SetCallback(this);
|
m_settings->SetCallback(this);
|
||||||
}
|
}
|
||||||
@@ -38,7 +51,11 @@ public:
|
|||||||
IFACEMETHODIMP_(void) Destroy() noexcept;
|
IFACEMETHODIMP_(void) Destroy() noexcept;
|
||||||
|
|
||||||
// IFancyZonesCallback
|
// IFancyZonesCallback
|
||||||
IFACEMETHODIMP_(bool) InMoveSize() noexcept { std::shared_lock readLock(m_lock); return m_inMoveSize; }
|
IFACEMETHODIMP_(bool) InMoveSize() noexcept
|
||||||
|
{
|
||||||
|
std::shared_lock readLock(m_lock);
|
||||||
|
return m_inMoveSize;
|
||||||
|
}
|
||||||
IFACEMETHODIMP_(void) MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept;
|
IFACEMETHODIMP_(void) MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept;
|
||||||
IFACEMETHODIMP_(void) MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept;
|
IFACEMETHODIMP_(void) MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept;
|
||||||
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
||||||
@@ -61,12 +78,16 @@ public:
|
|||||||
const auto nB = (tmp & 0xFF);
|
const auto nB = (tmp & 0xFF);
|
||||||
return RGB(nR, nG, nB);
|
return RGB(nR, nG, nB);
|
||||||
}
|
}
|
||||||
IFACEMETHODIMP_(GUID) GetCurrentMonitorZoneSetId(HMONITOR monitor) noexcept
|
IFACEMETHODIMP_(IZoneWindow*)GetParentZoneWindow(HMONITOR monitor) noexcept
|
||||||
{
|
{
|
||||||
if (auto it = m_zoneWindowMap.find(monitor); it != m_zoneWindowMap.end() && it->second->ActiveZoneSet()) {
|
//NOTE: as public method it's unsafe without lock, but it's called from AddZoneWindow through making ZoneWindow that causes deadlock
|
||||||
return it->second->ActiveZoneSet()->Id();
|
//TODO: needs refactoring
|
||||||
|
auto it = m_zoneWindowMap.find(monitor);
|
||||||
|
if (it != m_zoneWindowMap.end())
|
||||||
|
{
|
||||||
|
return it->second.get();
|
||||||
}
|
}
|
||||||
return GUID_NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
IFACEMETHODIMP_(int) GetZoneHighlightOpacity() noexcept
|
IFACEMETHODIMP_(int) GetZoneHighlightOpacity() noexcept
|
||||||
{
|
{
|
||||||
@@ -85,16 +106,25 @@ private:
|
|||||||
struct require_read_lock
|
struct require_read_lock
|
||||||
{
|
{
|
||||||
template<typename T>
|
template<typename T>
|
||||||
require_read_lock(const std::shared_lock<T>& lock) { lock; }
|
require_read_lock(const std::shared_lock<T>& lock)
|
||||||
|
{
|
||||||
|
lock;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
require_read_lock(const std::unique_lock<T>& lock) { lock; }
|
require_read_lock(const std::unique_lock<T>& lock)
|
||||||
|
{
|
||||||
|
lock;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct require_write_lock
|
struct require_write_lock
|
||||||
{
|
{
|
||||||
template<typename T>
|
template<typename T>
|
||||||
require_write_lock(const std::unique_lock<T>& lock) { lock; }
|
require_write_lock(const std::unique_lock<T>& lock)
|
||||||
|
{
|
||||||
|
lock;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsInterestingWindow(HWND window) noexcept;
|
bool IsInterestingWindow(HWND window) noexcept;
|
||||||
@@ -107,6 +137,7 @@ private:
|
|||||||
void MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept;
|
void MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept;
|
||||||
void MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept;
|
void MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept;
|
||||||
void HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) noexcept;
|
void HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) noexcept;
|
||||||
|
void OnEditorExitEvent() noexcept;
|
||||||
|
|
||||||
const HINSTANCE m_hinstance{};
|
const HINSTANCE m_hinstance{};
|
||||||
|
|
||||||
@@ -115,11 +146,11 @@ private:
|
|||||||
mutable std::shared_mutex m_lock;
|
mutable std::shared_mutex m_lock;
|
||||||
HWND m_window{};
|
HWND m_window{};
|
||||||
HWND m_windowMoveSize{}; // The window that is being moved/sized
|
HWND m_windowMoveSize{}; // The window that is being moved/sized
|
||||||
bool m_inMoveSize{}; // Whether or not a move/size operation is currently active
|
bool m_inMoveSize{}; // Whether or not a move/size operation is currently active
|
||||||
bool m_dragEnabled{}; // True if we should be showing zone hints while dragging
|
bool m_dragEnabled{}; // True if we should be showing zone hints while dragging
|
||||||
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
||||||
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
|
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
|
||||||
IFancyZonesSettings* m_settings{};
|
winrt::com_ptr<IFancyZonesSettings> m_settings{};
|
||||||
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
|
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
|
||||||
std::unordered_map<GUID, bool> m_virtualDesktopIds;
|
std::unordered_map<GUID, bool> m_virtualDesktopIds;
|
||||||
wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on
|
wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on
|
||||||
@@ -159,18 +190,21 @@ IFACEMETHODIMP_(void) FancyZones::Run() noexcept
|
|||||||
BufferedPaintInit();
|
BufferedPaintInit();
|
||||||
|
|
||||||
m_window = CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones", L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, m_hinstance, this);
|
m_window = CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones", L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, m_hinstance, this);
|
||||||
if (!m_window) return;
|
if (!m_window)
|
||||||
|
return;
|
||||||
|
|
||||||
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
||||||
|
|
||||||
VirtualDesktopInitialize();
|
VirtualDesktopInitialize();
|
||||||
|
|
||||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[]{
|
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [] {
|
||||||
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
|
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
|
||||||
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
|
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
|
||||||
}}).wait();
|
} })
|
||||||
|
.wait();
|
||||||
|
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops", 0, KEY_ALL_ACCESS, &m_virtualDesktopsRegKey) == ERROR_SUCCESS) {
|
if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops", 0, KEY_ALL_ACCESS, &m_virtualDesktopsRegKey) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
m_virtualDesktopTrackerThread.submit(
|
m_virtualDesktopTrackerThread.submit(
|
||||||
OnThreadExecutor::task_t{ std::bind(&FancyZones::HandleVirtualDesktopUpdates, this, m_terminateVirtualDesktopTrackerEvent.get()) });
|
OnThreadExecutor::task_t{ std::bind(&FancyZones::HandleVirtualDesktopUpdates, this, m_terminateVirtualDesktopTrackerEvent.get()) });
|
||||||
@@ -188,10 +222,12 @@ IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept
|
|||||||
DestroyWindow(m_window);
|
DestroyWindow(m_window);
|
||||||
m_window = nullptr;
|
m_window = nullptr;
|
||||||
}
|
}
|
||||||
if (m_terminateVirtualDesktopTrackerEvent) {
|
if (m_terminateVirtualDesktopTrackerEvent)
|
||||||
|
{
|
||||||
SetEvent(m_terminateVirtualDesktopTrackerEvent.get());
|
SetEvent(m_terminateVirtualDesktopTrackerEvent.get());
|
||||||
}
|
}
|
||||||
if (m_virtualDesktopsRegKey) {
|
if (m_virtualDesktopsRegKey)
|
||||||
|
{
|
||||||
RegCloseKey(m_virtualDesktopsRegKey);
|
RegCloseKey(m_virtualDesktopsRegKey);
|
||||||
m_virtualDesktopsRegKey = nullptr;
|
m_virtualDesktopsRegKey = nullptr;
|
||||||
}
|
}
|
||||||
@@ -229,6 +265,7 @@ IFACEMETHODIMP_(void) FancyZones::VirtualDesktopChanged() noexcept
|
|||||||
{
|
{
|
||||||
// VirtualDesktopChanged is called from another thread but results in new windows being created.
|
// VirtualDesktopChanged is called from another thread but results in new windows being created.
|
||||||
// Jump over to the UI thread to handle it.
|
// Jump over to the UI thread to handle it.
|
||||||
|
std::shared_lock readLock(m_lock);
|
||||||
PostMessage(m_window, WM_PRIV_VDCHANGED, 0, 0);
|
PostMessage(m_window, WM_PRIV_VDCHANGED, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,14 +280,28 @@ IFACEMETHODIMP_(void) FancyZones::WindowCreated(HWND window) noexcept
|
|||||||
{
|
{
|
||||||
if (m_settings->GetSettings().appLastZone_moveWindows && IsInterestingWindow(window))
|
if (m_settings->GetSettings().appLastZone_moveWindows && IsInterestingWindow(window))
|
||||||
{
|
{
|
||||||
auto processPath = get_process_path(window);
|
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
if (!processPath.empty())
|
if (monitor)
|
||||||
{
|
{
|
||||||
INT zoneIndex = -1;
|
auto zoneWindow = m_zoneWindowMap.find(monitor);
|
||||||
LRESULT res = RegistryHelpers::GetAppLastZone(window, processPath.data(), &zoneIndex);
|
if (zoneWindow != m_zoneWindowMap.end())
|
||||||
if ((res == ERROR_SUCCESS) && (zoneIndex != -1))
|
|
||||||
{
|
{
|
||||||
MoveWindowIntoZoneByIndex(window, zoneIndex);
|
const auto& zoneWindowPtr = zoneWindow->second;
|
||||||
|
const auto activeZoneSet = zoneWindowPtr->ActiveZoneSet();
|
||||||
|
if (activeZoneSet)
|
||||||
|
{
|
||||||
|
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string guidString;
|
||||||
|
if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString)))
|
||||||
|
{
|
||||||
|
int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindowPtr->UniqueId(), guidString.get());
|
||||||
|
if (zoneIndex != -1)
|
||||||
|
{
|
||||||
|
MoveWindowIntoZoneByIndex(window, zoneIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,11 +397,12 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
MONITORINFOEX mi;
|
MONITORINFOEX mi;
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
|
|
||||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[&]{
|
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
||||||
GetMonitorInfo(monitor, &mi);
|
GetMonitorInfo(monitor, &mi);
|
||||||
}}).wait();
|
} })
|
||||||
|
.wait();
|
||||||
|
|
||||||
if(use_cursorpos_editor_startupscreen)
|
if (use_cursorpos_editor_startupscreen)
|
||||||
{
|
{
|
||||||
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
||||||
}
|
}
|
||||||
@@ -359,6 +411,11 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto zoneWindow = iter->second;
|
||||||
|
|
||||||
|
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||||
|
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||||
|
|
||||||
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
|
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
|
||||||
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
|
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
|
||||||
|
|
||||||
@@ -367,22 +424,24 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
const auto y = mi.rcMonitor.top + taskbar_y_offset;
|
const auto y = mi.rcMonitor.top + taskbar_y_offset;
|
||||||
const auto width = mi.rcWork.right - mi.rcWork.left;
|
const auto width = mi.rcWork.right - mi.rcWork.left;
|
||||||
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
||||||
const std::wstring editorLocation =
|
const std::wstring editorLocation =
|
||||||
std::to_wstring(x) + L"_" +
|
std::to_wstring(x) + L"_" +
|
||||||
std::to_wstring(y) + L"_" +
|
std::to_wstring(y) + L"_" +
|
||||||
std::to_wstring(width) + L"_" +
|
std::to_wstring(width) + L"_" +
|
||||||
std::to_wstring(height);
|
std::to_wstring(height);
|
||||||
|
|
||||||
const auto activeZoneSet = iter->second->ActiveZoneSet();
|
const auto& deviceInfo = fancyZonesData.GetDeviceInfoMap().at(zoneWindow->UniqueId());
|
||||||
const std::wstring layoutID = activeZoneSet ? std::to_wstring(activeZoneSet->LayoutId()) : L"0";
|
|
||||||
|
JSONHelpers::DeviceInfoJSON deviceInfoJson{ zoneWindow->UniqueId(), deviceInfo };
|
||||||
|
fancyZonesData.SerializeDeviceInfoToTmpFile(deviceInfoJson, ZoneWindowUtils::GetActiveZoneSetTmpPath());
|
||||||
|
|
||||||
const std::wstring params =
|
const std::wstring params =
|
||||||
iter->second->UniqueId() + L" " +
|
/*1*/ std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
|
||||||
layoutID + L" " +
|
/*2*/ editorLocation + L" " +
|
||||||
std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
|
/*3*/ zoneWindow->WorkAreaKey() + L" " +
|
||||||
editorLocation + L" " +
|
/*4*/ ZoneWindowUtils::GetActiveZoneSetTmpPath() + L" " +
|
||||||
iter->second->WorkAreaKey() + L" " +
|
/*5*/ ZoneWindowUtils::GetAppliedZoneSetTmpPath() + L" " +
|
||||||
std::to_wstring(static_cast<float>(dpi_x) / DPIAware::DEFAULT_DPI);
|
/*6*/ ZoneWindowUtils::GetCustomZoneSetsTmpPath();
|
||||||
|
|
||||||
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
||||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||||
@@ -394,8 +453,7 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
// Launch the editor on a background thread
|
// Launch the editor on a background thread
|
||||||
// Wait for the editor's process to exit
|
// Wait for the editor's process to exit
|
||||||
// Post back to the main thread to update
|
// Post back to the main thread to update
|
||||||
std::thread waitForEditorThread([window = m_window, processHandle = sei.hProcess, terminateEditorEvent = m_terminateEditorEvent.get()]()
|
std::thread waitForEditorThread([window = m_window, processHandle = sei.hProcess, terminateEditorEvent = m_terminateEditorEvent.get()]() {
|
||||||
{
|
|
||||||
HANDLE waitEvents[2] = { processHandle, terminateEditorEvent };
|
HANDLE waitEvents[2] = { processHandle, terminateEditorEvent };
|
||||||
auto result = WaitForMultipleObjects(2, waitEvents, false, INFINITE);
|
auto result = WaitForMultipleObjects(2, waitEvents, false, INFINITE);
|
||||||
if (result == WAIT_OBJECT_0 + 0)
|
if (result == WAIT_OBJECT_0 + 0)
|
||||||
@@ -419,6 +477,7 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
|
|
||||||
void FancyZones::SettingsChanged() noexcept
|
void FancyZones::SettingsChanged() noexcept
|
||||||
{
|
{
|
||||||
|
std::shared_lock readLock(m_lock);
|
||||||
// Update the hotkey
|
// Update the hotkey
|
||||||
UnregisterHotKey(m_window, 1);
|
UnregisterHotKey(m_window, 1);
|
||||||
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
||||||
@@ -475,7 +534,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
|||||||
{
|
{
|
||||||
if (lparam == static_cast<LPARAM>(EditorExitKind::Exit))
|
if (lparam == static_cast<LPARAM>(EditorExitKind::Exit))
|
||||||
{
|
{
|
||||||
// Don't reload settings if we terminated the editor
|
OnEditorExitEvent();
|
||||||
OnDisplayChange(DisplayChangeType::Editor);
|
OnDisplayChange(DisplayChangeType::Editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,10 +563,10 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
|||||||
// the first virtual desktop switch happens. If the user hasn't switched virtual desktops in this session
|
// the first virtual desktop switch happens. If the user hasn't switched virtual desktops in this session
|
||||||
// then this value will be empty. This means loading the first virtual desktop's configuration can be
|
// then this value will be empty. This means loading the first virtual desktop's configuration can be
|
||||||
// funky the first time we load up at boot since the user will not have switched virtual desktops yet.
|
// funky the first time we load up at boot since the user will not have switched virtual desktops yet.
|
||||||
std::shared_lock readLock(m_lock);
|
|
||||||
GUID currentVirtualDesktopId{};
|
GUID currentVirtualDesktopId{};
|
||||||
if (SUCCEEDED(RegistryHelpers::GetCurrentVirtualDesktop(¤tVirtualDesktopId)))
|
if (SUCCEEDED(RegistryHelpers::GetCurrentVirtualDesktop(¤tVirtualDesktopId)))
|
||||||
{
|
{
|
||||||
|
std::unique_lock writeLock(m_lock);
|
||||||
m_currentVirtualDesktopId = currentVirtualDesktopId;
|
m_currentVirtualDesktopId = currentVirtualDesktopId;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -548,13 +607,19 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
|||||||
wil::unique_cotaskmem_string virtualDesktopId;
|
wil::unique_cotaskmem_string virtualDesktopId;
|
||||||
if (SUCCEEDED_LOG(StringFromCLSID(m_currentVirtualDesktopId, &virtualDesktopId)))
|
if (SUCCEEDED_LOG(StringFromCLSID(m_currentVirtualDesktopId, &virtualDesktopId)))
|
||||||
{
|
{
|
||||||
|
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||||
bool newVirtualDesktop = true;
|
bool newVirtualDesktop = true;
|
||||||
if (auto it = m_virtualDesktopIds.find(m_currentVirtualDesktopId); it != end(m_virtualDesktopIds)) {
|
|
||||||
newVirtualDesktop = it->second;
|
|
||||||
}
|
|
||||||
const bool flash = m_settings->GetSettings().zoneSetChange_flashZones && newVirtualDesktop;
|
|
||||||
|
|
||||||
if (auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, deviceId, virtualDesktopId.get(), flash))
|
auto it = m_virtualDesktopIds.find(m_currentVirtualDesktopId);
|
||||||
|
if (it != end(m_virtualDesktopIds))
|
||||||
|
{
|
||||||
|
newVirtualDesktop = it->second;
|
||||||
|
JSONHelpers::FancyZonesDataInstance().SetActiveDeviceId(uniqueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool flash = m_settings->GetSettings().zoneSetChange_flashZones && newVirtualDesktop;
|
||||||
|
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash);
|
||||||
|
if (zoneWindow)
|
||||||
{
|
{
|
||||||
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
||||||
}
|
}
|
||||||
@@ -567,12 +632,14 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
|
|||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
if (window != m_windowMoveSize)
|
if (window != m_windowMoveSize)
|
||||||
{
|
{
|
||||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
|
if (monitor)
|
||||||
{
|
{
|
||||||
auto iter = m_zoneWindowMap.find(monitor);
|
auto iter = m_zoneWindowMap.find(monitor);
|
||||||
if (iter != m_zoneWindowMap.end())
|
if (iter != m_zoneWindowMap.end())
|
||||||
{
|
{
|
||||||
iter->second->MoveWindowIntoZoneByIndex(window, index);
|
const auto& zoneWindowPtr = iter->second;
|
||||||
|
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,7 +656,7 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return thisRef ? thisRef->WndProc(window, message, wparam, lparam) :
|
return thisRef ? thisRef->WndProc(window, message, wparam, lparam) :
|
||||||
DefWindowProc(window, message, wparam, lparam);
|
DefWindowProc(window, message, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZones::IsInterestingWindow(HWND window) noexcept
|
bool FancyZones::IsInterestingWindow(HWND window) noexcept
|
||||||
@@ -616,8 +683,7 @@ bool FancyZones::IsInterestingWindow(HWND window) noexcept
|
|||||||
|
|
||||||
void FancyZones::UpdateZoneWindows() noexcept
|
void FancyZones::UpdateZoneWindows() noexcept
|
||||||
{
|
{
|
||||||
auto callback = [](HMONITOR monitor, HDC, RECT *, LPARAM data) -> BOOL
|
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
||||||
{
|
|
||||||
MONITORINFOEX mi;
|
MONITORINFOEX mi;
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
if (GetMonitorInfo(monitor, &mi))
|
if (GetMonitorInfo(monitor, &mi))
|
||||||
@@ -643,8 +709,8 @@ void FancyZones::UpdateZoneWindows() noexcept
|
|||||||
if (!deviceId)
|
if (!deviceId)
|
||||||
{
|
{
|
||||||
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
||||||
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
||||||
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
||||||
}
|
}
|
||||||
|
|
||||||
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
||||||
@@ -659,14 +725,13 @@ void FancyZones::UpdateZoneWindows() noexcept
|
|||||||
|
|
||||||
void FancyZones::MoveWindowsOnDisplayChange() noexcept
|
void FancyZones::MoveWindowsOnDisplayChange() noexcept
|
||||||
{
|
{
|
||||||
auto callback = [](HWND window, LPARAM data) -> BOOL
|
auto callback = [](HWND window, LPARAM data) -> BOOL {
|
||||||
{
|
|
||||||
int i = static_cast<int>(reinterpret_cast<UINT_PTR>(::GetProp(window, ZONE_STAMP)));
|
int i = static_cast<int>(reinterpret_cast<UINT_PTR>(::GetProp(window, ZONE_STAMP)));
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
{
|
{
|
||||||
// i is off by 1 since 0 is special.
|
// i is off by 1 since 0 is special.
|
||||||
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
||||||
strongThis->MoveWindowIntoZoneByIndex(window, i-1);
|
strongThis->MoveWindowIntoZoneByIndex(window, i - 1);
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
};
|
};
|
||||||
@@ -683,7 +748,7 @@ void FancyZones::UpdateDragState(require_write_lock) noexcept
|
|||||||
const bool mouseX2 = GetAsyncKeyState(VK_XBUTTON2) & 0x8000;
|
const bool mouseX2 = GetAsyncKeyState(VK_XBUTTON2) & 0x8000;
|
||||||
|
|
||||||
// Note, Middle, X1 and X2 can also be used in addition to R/L
|
// Note, Middle, X1 and X2 can also be used in addition to R/L
|
||||||
bool mouse = mouseM | mouseX1 | mouseX2;
|
bool mouse = mouseM | mouseX1 | mouseX2;
|
||||||
// If the user has swapped their Right and Left Buttons, use the "Right" equivalent
|
// If the user has swapped their Right and Left Buttons, use the "Right" equivalent
|
||||||
if (GetSystemMetrics(SM_SWAPBUTTON))
|
if (GetSystemMetrics(SM_SWAPBUTTON))
|
||||||
{
|
{
|
||||||
@@ -709,13 +774,16 @@ void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
|||||||
auto window = GetForegroundWindow();
|
auto window = GetForegroundWindow();
|
||||||
if (IsInterestingWindow(window))
|
if (IsInterestingWindow(window))
|
||||||
{
|
{
|
||||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
|
if (monitor)
|
||||||
{
|
{
|
||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
|
|
||||||
auto iter = m_zoneWindowMap.find(monitor);
|
auto iter = m_zoneWindowMap.find(monitor);
|
||||||
if (iter != m_zoneWindowMap.end())
|
if (iter != m_zoneWindowMap.end())
|
||||||
{
|
{
|
||||||
iter->second->CycleActiveZoneSet(vkCode);
|
const auto& zoneWindowPtr = iter->second;
|
||||||
|
zoneWindowPtr->CycleActiveZoneSet(vkCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -726,13 +794,16 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
|||||||
auto window = GetForegroundWindow();
|
auto window = GetForegroundWindow();
|
||||||
if (IsInterestingWindow(window))
|
if (IsInterestingWindow(window))
|
||||||
{
|
{
|
||||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
|
if (monitor)
|
||||||
{
|
{
|
||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
|
|
||||||
auto iter = m_zoneWindowMap.find(monitor);
|
auto iter = m_zoneWindowMap.find(monitor);
|
||||||
if (iter != m_zoneWindowMap.end())
|
if (iter != m_zoneWindowMap.end())
|
||||||
{
|
{
|
||||||
iter->second->MoveWindowIntoZoneByDirection(window, vkCode);
|
const auto& zoneWindowPtr = iter->second;
|
||||||
|
zoneWindowPtr->MoveWindowIntoZoneByDirection(window, vkCode);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,10 +869,23 @@ void FancyZones::MoveSizeEndInternal(HWND window, POINT const& ptScreen, require
|
|||||||
{
|
{
|
||||||
::RemoveProp(window, ZONE_STAMP);
|
::RemoveProp(window, ZONE_STAMP);
|
||||||
|
|
||||||
auto processPath = get_process_path(window);
|
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
if (!processPath.empty())
|
if (monitor)
|
||||||
{
|
{
|
||||||
RegistryHelpers::SaveAppLastZone(window, processPath.data(), -1);
|
auto zoneWindow = m_zoneWindowMap.find(monitor);
|
||||||
|
if (zoneWindow != m_zoneWindowMap.end())
|
||||||
|
{
|
||||||
|
const auto zoneWindowPtr = zoneWindow->second;
|
||||||
|
const auto activeZoneSet = zoneWindowPtr->ActiveZoneSet();
|
||||||
|
if (activeZoneSet)
|
||||||
|
{
|
||||||
|
wil::unique_cotaskmem_string guidString;
|
||||||
|
if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString)))
|
||||||
|
{
|
||||||
|
JSONHelpers::FancyZonesDataInstance().RemoveAppLastZone(window, zoneWindowPtr->UniqueId(), guidString.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -853,38 +937,48 @@ void FancyZones::HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) no
|
|||||||
{
|
{
|
||||||
HANDLE regKeyEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
HANDLE regKeyEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
HANDLE events[2] = { regKeyEvent, fancyZonesDestroyedEvent };
|
HANDLE events[2] = { regKeyEvent, fancyZonesDestroyedEvent };
|
||||||
while (1) {
|
while (1)
|
||||||
if (RegNotifyChangeKeyValue(HKEY_CURRENT_USER, TRUE, REG_NOTIFY_CHANGE_LAST_SET, regKeyEvent, TRUE) != ERROR_SUCCESS) {
|
{
|
||||||
|
if (RegNotifyChangeKeyValue(HKEY_CURRENT_USER, TRUE, REG_NOTIFY_CHANGE_LAST_SET, regKeyEvent, TRUE) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (WaitForMultipleObjects(2, events, FALSE, INFINITE) != (WAIT_OBJECT_0 + 0)) {
|
if (WaitForMultipleObjects(2, events, FALSE, INFINITE) != (WAIT_OBJECT_0 + 0))
|
||||||
|
{
|
||||||
// if fancyZonesDestroyedEvent is signalized or WaitForMultipleObjects failed, terminate thread execution
|
// if fancyZonesDestroyedEvent is signalized or WaitForMultipleObjects failed, terminate thread execution
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DWORD bufferCapacity;
|
DWORD bufferCapacity;
|
||||||
const WCHAR* key = L"VirtualDesktopIDs";
|
const WCHAR* key = L"VirtualDesktopIDs";
|
||||||
// request regkey binary buffer capacity only
|
// request regkey binary buffer capacity only
|
||||||
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS) {
|
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(bufferCapacity);
|
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(bufferCapacity);
|
||||||
// request regkey binary content
|
// request regkey binary content
|
||||||
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS) {
|
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int guidSize = sizeof(GUID);
|
const int guidSize = sizeof(GUID);
|
||||||
std::unordered_map<GUID, bool> temp;
|
std::unordered_map<GUID, bool> temp;
|
||||||
temp.reserve(bufferCapacity / guidSize);
|
temp.reserve(bufferCapacity / guidSize);
|
||||||
for (size_t i = 0; i < bufferCapacity; i += guidSize) {
|
for (size_t i = 0; i < bufferCapacity; i += guidSize)
|
||||||
GUID *guid = reinterpret_cast<GUID*>(buffer.get() + i);
|
{
|
||||||
|
GUID* guid = reinterpret_cast<GUID*>(buffer.get() + i);
|
||||||
temp[*guid] = true;
|
temp[*guid] = true;
|
||||||
}
|
}
|
||||||
std::unique_lock writeLock(m_lock);
|
std::unique_lock writeLock(m_lock);
|
||||||
for (auto it = begin(m_virtualDesktopIds); it != end(m_virtualDesktopIds);) {
|
for (auto it = begin(m_virtualDesktopIds); it != end(m_virtualDesktopIds);)
|
||||||
if (auto iter = temp.find(it->first); iter == temp.end()) {
|
{
|
||||||
|
auto iter = temp.find(it->first);
|
||||||
|
if (iter == temp.end())
|
||||||
|
{
|
||||||
it = m_virtualDesktopIds.erase(it); // virtual desktop closed, remove it from map
|
it = m_virtualDesktopIds.erase(it); // virtual desktop closed, remove it from map
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
temp.erase(it->first); // virtual desktop already in map, skip it
|
temp.erase(it->first); // virtual desktop already in map, skip it
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -894,7 +988,21 @@ void FancyZones::HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) no
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
|
void FancyZones::OnEditorExitEvent() noexcept
|
||||||
{
|
{
|
||||||
|
// Colect information about changes in zone layout after editor exited.
|
||||||
|
JSONHelpers::FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(ZoneWindowUtils::GetActiveZoneSetTmpPath());
|
||||||
|
JSONHelpers::FancyZonesDataInstance().ParseDeletedCustomZoneSetsFromTmpFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||||
|
JSONHelpers::FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
|
||||||
|
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept
|
||||||
|
{
|
||||||
|
if (!settings)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return winrt::make_self<FancyZones>(hinstance, settings);
|
return winrt::make_self<FancyZones>(hinstance, settings);
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,7 @@
|
|||||||
|
|
||||||
interface IZoneWindow;
|
interface IZoneWindow;
|
||||||
interface IFancyZonesSettings;
|
interface IFancyZonesSettings;
|
||||||
|
interface IZoneSet;
|
||||||
enum class DisplayChangeType
|
|
||||||
{
|
|
||||||
WorkArea,
|
|
||||||
DisplayChange,
|
|
||||||
VirtualDesktop,
|
|
||||||
Editor,
|
|
||||||
Initialization
|
|
||||||
};
|
|
||||||
|
|
||||||
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
||||||
{
|
{
|
||||||
@@ -35,8 +27,8 @@ interface __declspec(uuid("{5C8D99D6-34B2-4F4A-A8E5-7483F6869775}")) IZoneWindow
|
|||||||
{
|
{
|
||||||
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
|
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
|
||||||
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)() = 0;
|
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)() = 0;
|
||||||
IFACEMETHOD_(GUID, GetCurrentMonitorZoneSetId)(HMONITOR monitor) = 0;
|
IFACEMETHOD_(IZoneWindow*, GetParentZoneWindow) (HMONITOR monitor) = 0;
|
||||||
IFACEMETHOD_(int, GetZoneHighlightOpacity)() = 0;
|
IFACEMETHOD_(int, GetZoneHighlightOpacity)() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept;
|
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept;
|
||||||
|
|||||||
@@ -92,6 +92,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="FancyZones.h" />
|
<ClInclude Include="FancyZones.h" />
|
||||||
|
<ClInclude Include="JsonHelpers.h" />
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
<ClInclude Include="RegistryHelpers.h" />
|
<ClInclude Include="RegistryHelpers.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
@@ -104,12 +105,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="FancyZones.cpp" />
|
<ClCompile Include="FancyZones.cpp" />
|
||||||
|
<ClCompile Include="JsonHelpers.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Settings.cpp" />
|
<ClCompile Include="Settings.cpp" />
|
||||||
<ClCompile Include="trace.cpp" />
|
<ClCompile Include="trace.cpp" />
|
||||||
|
<ClCompile Include="util.cpp" />
|
||||||
<ClCompile Include="Zone.cpp" />
|
<ClCompile Include="Zone.cpp" />
|
||||||
<ClCompile Include="ZoneSet.cpp" />
|
<ClCompile Include="ZoneSet.cpp" />
|
||||||
<ClCompile Include="ZoneWindow.cpp" />
|
<ClCompile Include="ZoneWindow.cpp" />
|
||||||
|
|||||||
@@ -45,6 +45,9 @@
|
|||||||
<ClInclude Include="trace.h">
|
<ClInclude Include="trace.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="JsonHelpers.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
@@ -68,6 +71,12 @@
|
|||||||
<ClCompile Include="trace.cpp">
|
<ClCompile Include="trace.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="JsonHelpers.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="util.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="fancyzones.rc">
|
<ResourceCompile Include="fancyzones.rc">
|
||||||
@@ -76,6 +85,5 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="fancyzones.def" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
950
src/modules/fancyzones/lib/JsonHelpers.cpp
Normal file
950
src/modules/fancyzones/lib/JsonHelpers.cpp
Normal file
@@ -0,0 +1,950 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "JsonHelpers.h"
|
||||||
|
#include "RegistryHelpers.h"
|
||||||
|
#include "ZoneSet.h"
|
||||||
|
|
||||||
|
#include <common/common.h>
|
||||||
|
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// From Settings.cs
|
||||||
|
constexpr int c_focusModelId = 0xFFFF;
|
||||||
|
constexpr int c_rowsModelId = 0xFFFE;
|
||||||
|
constexpr int c_columnsModelId = 0xFFFD;
|
||||||
|
constexpr int c_gridModelId = 0xFFFC;
|
||||||
|
constexpr int c_priorityGridModelId = 0xFFFB;
|
||||||
|
constexpr int c_blankCustomModelId = 0xFFFA;
|
||||||
|
|
||||||
|
const wchar_t* FANCY_ZONES_DATA_FILE = L"zones-settings.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace JSONHelpers
|
||||||
|
{
|
||||||
|
json::JsonArray NumVecToJsonArray(const std::vector<int>& vec)
|
||||||
|
{
|
||||||
|
json::JsonArray arr;
|
||||||
|
for (const auto& val : vec)
|
||||||
|
{
|
||||||
|
arr.Append(json::JsonValue::CreateNumberValue(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> JsonArrayToNumVec(const json::JsonArray& arr)
|
||||||
|
{
|
||||||
|
std::vector<int> vec;
|
||||||
|
for (const auto& val : arr)
|
||||||
|
{
|
||||||
|
vec.emplace_back(static_cast<int>(val.GetNumber()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoneSetLayoutType TypeFromLayoutId(int layoutID)
|
||||||
|
{
|
||||||
|
switch (layoutID)
|
||||||
|
{
|
||||||
|
case c_focusModelId:
|
||||||
|
return ZoneSetLayoutType::Focus;
|
||||||
|
case c_columnsModelId:
|
||||||
|
return ZoneSetLayoutType::Columns;
|
||||||
|
case c_rowsModelId:
|
||||||
|
return ZoneSetLayoutType::Rows;
|
||||||
|
case c_gridModelId:
|
||||||
|
return ZoneSetLayoutType::Grid;
|
||||||
|
case c_priorityGridModelId:
|
||||||
|
return ZoneSetLayoutType::PriorityGrid;
|
||||||
|
case c_blankCustomModelId:
|
||||||
|
return ZoneSetLayoutType::Blank;
|
||||||
|
default:
|
||||||
|
return ZoneSetLayoutType::Custom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring TypeToString(ZoneSetLayoutType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ZoneSetLayoutType::Blank:
|
||||||
|
return L"blank";
|
||||||
|
case ZoneSetLayoutType::Focus:
|
||||||
|
return L"focus";
|
||||||
|
case ZoneSetLayoutType::Columns:
|
||||||
|
return L"columns";
|
||||||
|
case ZoneSetLayoutType::Rows:
|
||||||
|
return L"rows";
|
||||||
|
case ZoneSetLayoutType::Grid:
|
||||||
|
return L"grid";
|
||||||
|
case ZoneSetLayoutType::PriorityGrid:
|
||||||
|
return L"priority-grid";
|
||||||
|
case ZoneSetLayoutType::Custom:
|
||||||
|
return L"custom";
|
||||||
|
default:
|
||||||
|
return L"TypeToString_ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoneSetLayoutType TypeFromString(const std::wstring& typeStr)
|
||||||
|
{
|
||||||
|
if (typeStr == L"focus")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Focus;
|
||||||
|
}
|
||||||
|
else if (typeStr == L"columns")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Columns;
|
||||||
|
}
|
||||||
|
else if (typeStr == L"rows")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Rows;
|
||||||
|
}
|
||||||
|
else if (typeStr == L"grid")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Grid;
|
||||||
|
}
|
||||||
|
else if (typeStr == L"priority-grid")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::PriorityGrid;
|
||||||
|
}
|
||||||
|
else if (typeStr == L"custom")
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Custom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return JSONHelpers::ZoneSetLayoutType::Blank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyZonesData& FancyZonesDataInstance()
|
||||||
|
{
|
||||||
|
static FancyZonesData instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyZonesData::FancyZonesData()
|
||||||
|
{
|
||||||
|
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"FancyZones");
|
||||||
|
jsonFilePath = result + L"\\" + std::wstring(FANCY_ZONES_DATA_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring& FancyZonesData::GetPersistFancyZonesJSONPath() const
|
||||||
|
{
|
||||||
|
return jsonFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject FancyZonesData::GetPersistFancyZonesJSON()
|
||||||
|
{
|
||||||
|
std::wstring save_file_path = GetPersistFancyZonesJSONPath();
|
||||||
|
|
||||||
|
auto result = json::from_file(save_file_path);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return json::JsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||||
|
{
|
||||||
|
if (!deviceInfoMap.contains(deviceId))
|
||||||
|
{
|
||||||
|
// Creates default entry in map when ZoneWindow is created
|
||||||
|
deviceInfoMap[deviceId] = DeviceInfoData{ ZoneSetData{ L"null", ZoneSetLayoutType::Blank } };
|
||||||
|
|
||||||
|
MigrateDeviceInfoFromRegistry(deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
|
||||||
|
{
|
||||||
|
// Clone information from source device if destination device is uninitialized (Blank).
|
||||||
|
auto& destInfo = deviceInfoMap[destination];
|
||||||
|
if (destInfo.activeZoneSet.type == ZoneSetLayoutType::Blank)
|
||||||
|
{
|
||||||
|
destInfo = deviceInfoMap[source];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FancyZonesData::GetAppLastZoneIndex(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
|
||||||
|
{
|
||||||
|
auto processPath = get_process_path(window);
|
||||||
|
if (!processPath.empty())
|
||||||
|
{
|
||||||
|
auto history = appZoneHistoryMap.find(processPath);
|
||||||
|
if (history != appZoneHistoryMap.end())
|
||||||
|
{
|
||||||
|
const auto& data = history->second;
|
||||||
|
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
|
||||||
|
{
|
||||||
|
return history->second.zoneIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
|
||||||
|
{
|
||||||
|
auto processPath = get_process_path(window);
|
||||||
|
if (!processPath.empty())
|
||||||
|
{
|
||||||
|
auto history = appZoneHistoryMap.find(processPath);
|
||||||
|
if (history != appZoneHistoryMap.end())
|
||||||
|
{
|
||||||
|
const auto& data = history->second;
|
||||||
|
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
|
||||||
|
{
|
||||||
|
appZoneHistoryMap.erase(processPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::SetAppLastZone(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, int zoneIndex)
|
||||||
|
{
|
||||||
|
auto processPath = get_process_path(window);
|
||||||
|
if (processPath.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
appZoneHistoryMap[processPath] = AppZoneHistoryData{ .zoneSetUuid = zoneSetId, .deviceId = deviceId, .zoneIndex = zoneIndex };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const ZoneSetData& data)
|
||||||
|
{
|
||||||
|
auto it = deviceInfoMap.find(deviceId);
|
||||||
|
if (it != deviceInfoMap.end())
|
||||||
|
{
|
||||||
|
it->second.activeZoneSet = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::SerializeDeviceInfoToTmpFile(const DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath) const
|
||||||
|
{
|
||||||
|
json::JsonObject deviceInfoJson = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(tmpFilePath, deviceInfoJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
|
{
|
||||||
|
if (std::filesystem::exists(tmpFilePath))
|
||||||
|
{
|
||||||
|
if (auto zoneSetJson = json::from_file(tmpFilePath); zoneSetJson.has_value())
|
||||||
|
{
|
||||||
|
if (auto deviceInfo = DeviceInfoJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
|
||||||
|
{
|
||||||
|
activeDeviceId = deviceInfo->deviceId;
|
||||||
|
deviceInfoMap[activeDeviceId] = std::move(deviceInfo->data);
|
||||||
|
DeleteTmpFile(tmpFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activeDeviceId.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
|
{
|
||||||
|
bool res = true;
|
||||||
|
if (std::filesystem::exists(tmpFilePath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (auto customZoneSetJson = json::from_file(tmpFilePath); customZoneSetJson.has_value())
|
||||||
|
{
|
||||||
|
if (auto customZoneSet = CustomZoneSetJSON::FromJson(customZoneSetJson.value()); customZoneSet.has_value())
|
||||||
|
{
|
||||||
|
customZoneSetsMap[customZoneSet->uuid] = std::move(customZoneSet->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteTmpFile(tmpFilePath);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
|
{
|
||||||
|
bool res = true;
|
||||||
|
if (std::filesystem::exists(tmpFilePath))
|
||||||
|
{
|
||||||
|
auto deletedZoneSetsJson = json::from_file(tmpFilePath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto deletedCustomZoneSets = deletedZoneSetsJson->GetNamedArray(L"deleted-custom-zone-sets");
|
||||||
|
for (auto zoneSet : deletedCustomZoneSets)
|
||||||
|
{
|
||||||
|
std::wstring uuid = L"{" + std::wstring{ zoneSet.GetString() } + L"}";
|
||||||
|
customZoneSetsMap.erase(std::wstring{ uuid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteTmpFile(tmpFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto appLastZones = fancyZonesDataJSON.GetNamedArray(L"app-zone-history");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < appLastZones.Size(); ++i)
|
||||||
|
{
|
||||||
|
json::JsonObject appLastZone = appLastZones.GetObjectAt(i);
|
||||||
|
if (auto appZoneHistory = AppZoneHistoryJSON::FromJson(appLastZone); appZoneHistory.has_value())
|
||||||
|
{
|
||||||
|
appZoneHistoryMap[appZoneHistory->appPath] = std::move(appZoneHistory->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonArray FancyZonesData::SerializeAppZoneHistory() const
|
||||||
|
{
|
||||||
|
json::JsonArray appHistoryArray;
|
||||||
|
|
||||||
|
for (const auto& [appPath, appZoneHistoryData] : appZoneHistoryMap)
|
||||||
|
{
|
||||||
|
appHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, appZoneHistoryData }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return appHistoryArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto devices = fancyZonesDataJSON.GetNamedArray(L"devices");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < devices.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (auto device = DeviceInfoJSON::DeviceInfoJSON::FromJson(devices.GetObjectAt(i)); device.has_value())
|
||||||
|
{
|
||||||
|
deviceInfoMap[device->deviceId] = std::move(device->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonArray FancyZonesData::SerializeDeviceInfos() const
|
||||||
|
{
|
||||||
|
json::JsonArray DeviceInfosJSON{};
|
||||||
|
|
||||||
|
for (const auto& [deviceID, deviceData] : deviceInfoMap)
|
||||||
|
{
|
||||||
|
if (deviceData.activeZoneSet.type != ZoneSetLayoutType::Blank) {
|
||||||
|
DeviceInfosJSON.Append(DeviceInfoJSON::DeviceInfoJSON::ToJson(DeviceInfoJSON{ deviceID, deviceData }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeviceInfosJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesData::ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto customZoneSets = fancyZonesDataJSON.GetNamedArray(L"custom-zone-sets");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < customZoneSets.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (auto zoneSet = CustomZoneSetJSON::FromJson(customZoneSets.GetObjectAt(i)); zoneSet.has_value())
|
||||||
|
{
|
||||||
|
customZoneSetsMap[zoneSet->uuid] = std::move(zoneSet->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonArray FancyZonesData::SerializeCustomZoneSets() const
|
||||||
|
{
|
||||||
|
json::JsonArray customZoneSetsJSON{};
|
||||||
|
|
||||||
|
for (const auto& [zoneSetId, zoneSetData] : customZoneSetsMap)
|
||||||
|
{
|
||||||
|
customZoneSetsJSON.Append(CustomZoneSetJSON::ToJson(CustomZoneSetJSON{ zoneSetId, zoneSetData }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return customZoneSetsJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::CustomZoneSetsToJsonFile(std::wstring_view filePath) const
|
||||||
|
{
|
||||||
|
const auto& customZoneSetsJson = SerializeCustomZoneSets();
|
||||||
|
json::JsonObject root{};
|
||||||
|
root.SetNamedValue(L"custom-zone-sets", customZoneSetsJson);
|
||||||
|
json::to_file(filePath, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::LoadFancyZonesData()
|
||||||
|
{
|
||||||
|
std::wstring jsonFilePath = GetPersistFancyZonesJSONPath();
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(jsonFilePath))
|
||||||
|
{
|
||||||
|
TmpMigrateAppliedZoneSetsFromRegistry();
|
||||||
|
|
||||||
|
// Custom zone sets have to be migrated after applied zone sets!
|
||||||
|
MigrateCustomZoneSetsFromRegistry();
|
||||||
|
|
||||||
|
SaveFancyZonesData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json::JsonObject fancyZonesDataJSON = GetPersistFancyZonesJSON();
|
||||||
|
|
||||||
|
ParseAppZoneHistory(fancyZonesDataJSON);
|
||||||
|
ParseDeviceInfos(fancyZonesDataJSON);
|
||||||
|
ParseCustomZoneSets(fancyZonesDataJSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::SaveFancyZonesData() const
|
||||||
|
{
|
||||||
|
json::JsonObject root{};
|
||||||
|
|
||||||
|
root.SetNamedValue(L"app-zone-history", SerializeAppZoneHistory());
|
||||||
|
root.SetNamedValue(L"devices", SerializeDeviceInfos());
|
||||||
|
root.SetNamedValue(L"custom-zone-sets", SerializeCustomZoneSets());
|
||||||
|
|
||||||
|
json::to_file(jsonFilePath, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::TmpMigrateAppliedZoneSetsFromRegistry()
|
||||||
|
{
|
||||||
|
std::wregex ex(L"^[0-9]{3,4}_[0-9]{3,4}$");
|
||||||
|
|
||||||
|
wchar_t key[256];
|
||||||
|
StringCchPrintf(key, ARRAYSIZE(key), L"%s", RegistryHelpers::REG_SETTINGS);
|
||||||
|
HKEY hkey;
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
wchar_t resolutionKey[256]{};
|
||||||
|
DWORD resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||||
|
DWORD i = 0;
|
||||||
|
while (RegEnumKeyW(hkey, i++, resolutionKey, resolutionKeyLength) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
std::wstring resolution{ resolutionKey };
|
||||||
|
wchar_t appliedZoneSetskey[256];
|
||||||
|
StringCchPrintf(appliedZoneSetskey, ARRAYSIZE(appliedZoneSetskey), L"%s\\%s", RegistryHelpers::REG_SETTINGS, resolutionKey);
|
||||||
|
HKEY appliedZoneSetsHkey;
|
||||||
|
if (std::regex_match(resolution, ex) && RegOpenKeyExW(HKEY_CURRENT_USER, appliedZoneSetskey, 0, KEY_ALL_ACCESS, &appliedZoneSetsHkey) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
ZoneSetPersistedDataOLD data;
|
||||||
|
DWORD dataSize = sizeof(data);
|
||||||
|
wchar_t value[256]{};
|
||||||
|
DWORD valueLength = ARRAYSIZE(value);
|
||||||
|
DWORD i = 0;
|
||||||
|
|
||||||
|
while (RegEnumValueW(appliedZoneSetsHkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
ZoneSetData appliedZoneSetData;
|
||||||
|
appliedZoneSetData.type = TypeFromLayoutId(data.LayoutId);
|
||||||
|
if (appliedZoneSetData.type != ZoneSetLayoutType::Custom)
|
||||||
|
{
|
||||||
|
appliedZoneSetData.uuid = std::wstring{ value };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// uuid is changed later to actual uuid when migrating custom zone sets
|
||||||
|
appliedZoneSetData.uuid = std::to_wstring(data.LayoutId);
|
||||||
|
}
|
||||||
|
appliedZoneSetsMap[value] = appliedZoneSetData;
|
||||||
|
dataSize = sizeof(data);
|
||||||
|
valueLength = ARRAYSIZE(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::MigrateDeviceInfoFromRegistry(const std::wstring& deviceId)
|
||||||
|
{
|
||||||
|
wchar_t key[256];
|
||||||
|
StringCchPrintf(key, ARRAYSIZE(key), L"%s\\%s", RegistryHelpers::REG_SETTINGS, deviceId.c_str());
|
||||||
|
|
||||||
|
wchar_t activeZoneSetId[256];
|
||||||
|
activeZoneSetId[0] = '\0';
|
||||||
|
DWORD bufferSize = sizeof(activeZoneSetId);
|
||||||
|
DWORD showSpacing = 1;
|
||||||
|
DWORD spacing = 16;
|
||||||
|
DWORD zoneCount = 3;
|
||||||
|
DWORD size = sizeof(DWORD);
|
||||||
|
|
||||||
|
SHRegGetUSValueW(key, L"ActiveZoneSetId", nullptr, &activeZoneSetId, &bufferSize, FALSE, nullptr, 0);
|
||||||
|
SHRegGetUSValueW(key, L"ShowSpacing", nullptr, &showSpacing, &size, FALSE, nullptr, 0);
|
||||||
|
SHRegGetUSValueW(key, L"Spacing", nullptr, &spacing, &size, FALSE, nullptr, 0);
|
||||||
|
SHRegGetUSValueW(key, L"ZoneCount", nullptr, &zoneCount, &size, FALSE, nullptr, 0);
|
||||||
|
|
||||||
|
if (appliedZoneSetsMap.contains(std::wstring{ activeZoneSetId }))
|
||||||
|
{
|
||||||
|
deviceInfoMap[deviceId] = DeviceInfoData{ appliedZoneSetsMap.at(std::wstring{ activeZoneSetId }), static_cast<bool>(showSpacing), static_cast<int>(spacing), static_cast<int>(zoneCount) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
|
||||||
|
{
|
||||||
|
wchar_t key[256];
|
||||||
|
StringCchPrintf(key, ARRAYSIZE(key), L"%s\\%s", RegistryHelpers::REG_SETTINGS, L"Layouts");
|
||||||
|
HKEY hkey;
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
BYTE data[256];
|
||||||
|
DWORD dataSize = ARRAYSIZE(data);
|
||||||
|
wchar_t value[256]{};
|
||||||
|
DWORD valueLength = ARRAYSIZE(value);
|
||||||
|
DWORD i = 0;
|
||||||
|
while (RegEnumValueW(hkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
CustomZoneSetData zoneSetData;
|
||||||
|
zoneSetData.name = std::wstring{ value };
|
||||||
|
zoneSetData.type = static_cast<CustomLayoutType>(data[2]);
|
||||||
|
// int version = data[0] * 256 + data[1]; - Not used anymore
|
||||||
|
|
||||||
|
std::wstring uuid = std::to_wstring(data[3] * 256 + data[4]);
|
||||||
|
auto it = std::find_if(appliedZoneSetsMap.begin(), appliedZoneSetsMap.end(), [&uuid](std::pair<std::wstring, ZoneSetData> zoneSetMap) {
|
||||||
|
return zoneSetMap.second.uuid.compare(uuid) == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != appliedZoneSetsMap.end())
|
||||||
|
{
|
||||||
|
it->second.uuid = uuid = it->first;
|
||||||
|
}
|
||||||
|
switch (zoneSetData.type)
|
||||||
|
{
|
||||||
|
case CustomLayoutType::Grid:
|
||||||
|
{
|
||||||
|
int j = 5;
|
||||||
|
GridLayoutInfo zoneSetInfo(GridLayoutInfo::Minimal{ .rows = data[j++], .columns = data[j++] });
|
||||||
|
|
||||||
|
for (int row = 0; row < zoneSetInfo.rows(); row++)
|
||||||
|
{
|
||||||
|
zoneSetInfo.rowsPercents()[row] = data[j++] * 256 + data[j++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int col = 0; col < zoneSetInfo.columns(); col++)
|
||||||
|
{
|
||||||
|
zoneSetInfo.columnsPercents()[col] = data[j++] * 256 + data[j++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row = 0; row < zoneSetInfo.rows(); row++)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < zoneSetInfo.columns(); col++)
|
||||||
|
{
|
||||||
|
zoneSetInfo.cellChildMap()[row][col] = data[j++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zoneSetData.info = zoneSetInfo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CustomLayoutType::Canvas:
|
||||||
|
{
|
||||||
|
CanvasLayoutInfo info;
|
||||||
|
|
||||||
|
int j = 5;
|
||||||
|
info.referenceWidth = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
info.referenceHeight = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
|
||||||
|
int count = data[j++];
|
||||||
|
info.zones.reserve(count);
|
||||||
|
while (count-- > 0)
|
||||||
|
{
|
||||||
|
int x = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
int y = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
int width = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
int height = data[j] * 256 + data[j + 1];
|
||||||
|
j += 2;
|
||||||
|
info.zones.push_back(CanvasLayoutInfo::Rect{
|
||||||
|
x, y, width, height });
|
||||||
|
}
|
||||||
|
zoneSetData.info = info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort(); // TODO(stefan): Exception safety
|
||||||
|
}
|
||||||
|
customZoneSetsMap[uuid] = zoneSetData;
|
||||||
|
|
||||||
|
valueLength = ARRAYSIZE(value);
|
||||||
|
dataSize = ARRAYSIZE(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject ZoneSetData::ToJson(const ZoneSetData& zoneSet)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
result.SetNamedValue(L"uuid", json::value(zoneSet.uuid));
|
||||||
|
result.SetNamedValue(L"type", json::value(TypeToString(zoneSet.type)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ZoneSetData> ZoneSetData::FromJson(const json::JsonObject& zoneSet)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ZoneSetData zoneSetData;
|
||||||
|
|
||||||
|
zoneSetData.uuid = zoneSet.GetNamedString(L"uuid");
|
||||||
|
zoneSetData.type = TypeFromString(std::wstring{ zoneSet.GetNamedString(L"type") });
|
||||||
|
|
||||||
|
return zoneSetData;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject AppZoneHistoryJSON::ToJson(const AppZoneHistoryJSON& appZoneHistory)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
result.SetNamedValue(L"app-path", json::value(appZoneHistory.appPath));
|
||||||
|
result.SetNamedValue(L"zone-index", json::value(appZoneHistory.data.zoneIndex));
|
||||||
|
result.SetNamedValue(L"device-id", json::value(appZoneHistory.data.deviceId));
|
||||||
|
result.SetNamedValue(L"zoneset-uuid", json::value(appZoneHistory.data.zoneSetUuid));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AppZoneHistoryJSON> AppZoneHistoryJSON::FromJson(const json::JsonObject& zoneSet)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AppZoneHistoryJSON result;
|
||||||
|
|
||||||
|
result.appPath = zoneSet.GetNamedString(L"app-path");
|
||||||
|
result.data.zoneIndex = static_cast<int>(zoneSet.GetNamedNumber(L"zone-index"));
|
||||||
|
result.data.deviceId = zoneSet.GetNamedString(L"device-id");
|
||||||
|
result.data.zoneSetUuid = zoneSet.GetNamedString(L"zoneset-uuid");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject DeviceInfoJSON::ToJson(const DeviceInfoJSON& device)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
result.SetNamedValue(L"device-id", json::value(device.deviceId));
|
||||||
|
result.SetNamedValue(L"active-zoneset", ZoneSetData::ToJson(device.data.activeZoneSet));
|
||||||
|
result.SetNamedValue(L"editor-show-spacing", json::value(device.data.showSpacing));
|
||||||
|
result.SetNamedValue(L"editor-spacing", json::value(device.data.spacing));
|
||||||
|
result.SetNamedValue(L"editor-zone-count", json::value(device.data.zoneCount));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DeviceInfoJSON> DeviceInfoJSON::FromJson(const json::JsonObject& device)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DeviceInfoJSON result;
|
||||||
|
|
||||||
|
result.deviceId = device.GetNamedString(L"device-id");
|
||||||
|
|
||||||
|
if (auto zoneSet = ZoneSetData::FromJson(device.GetNamedObject(L"active-zoneset")); zoneSet.has_value())
|
||||||
|
{
|
||||||
|
result.data.activeZoneSet = std::move(zoneSet.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.data.showSpacing = device.GetNamedBoolean(L"editor-show-spacing");
|
||||||
|
result.data.spacing = static_cast<int>(device.GetNamedNumber(L"editor-spacing"));
|
||||||
|
result.data.zoneCount = static_cast<int>(
|
||||||
|
device.GetNamedNumber(L"editor-zone-count"));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject CanvasLayoutInfo::ToJson(const CanvasLayoutInfo& canvasInfo)
|
||||||
|
{
|
||||||
|
json::JsonObject infoJson{};
|
||||||
|
infoJson.SetNamedValue(L"ref-width", json::value(canvasInfo.referenceWidth));
|
||||||
|
infoJson.SetNamedValue(L"ref-height", json::value(canvasInfo.referenceHeight));
|
||||||
|
json::JsonArray zonesJson;
|
||||||
|
|
||||||
|
for (const auto& [x, y, width, height] : canvasInfo.zones)
|
||||||
|
{
|
||||||
|
json::JsonObject zoneJson;
|
||||||
|
zoneJson.SetNamedValue(L"X", json::value(x));
|
||||||
|
zoneJson.SetNamedValue(L"Y", json::value(y));
|
||||||
|
zoneJson.SetNamedValue(L"width", json::value(width));
|
||||||
|
zoneJson.SetNamedValue(L"height", json::value(height));
|
||||||
|
zonesJson.Append(zoneJson);
|
||||||
|
}
|
||||||
|
infoJson.SetNamedValue(L"zones", zonesJson);
|
||||||
|
return infoJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CanvasLayoutInfo> CanvasLayoutInfo::FromJson(const json::JsonObject& infoJson)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CanvasLayoutInfo info;
|
||||||
|
info.referenceWidth = static_cast<int>(infoJson.GetNamedNumber(L"ref-width"));
|
||||||
|
info.referenceHeight = static_cast<int>(infoJson.GetNamedNumber(L"ref-height"));
|
||||||
|
json::JsonArray zonesJson = infoJson.GetNamedArray(L"zones");
|
||||||
|
uint32_t size = zonesJson.Size();
|
||||||
|
info.zones.reserve(size);
|
||||||
|
for (uint32_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
json::JsonObject zoneJson = zonesJson.GetObjectAt(i);
|
||||||
|
const int x = static_cast<int>(zoneJson.GetNamedNumber(L"X"));
|
||||||
|
const int y = static_cast<int>(zoneJson.GetNamedNumber(L"Y"));
|
||||||
|
const int width = static_cast<int>(zoneJson.GetNamedNumber(L"width"));
|
||||||
|
const int height = static_cast<int>(zoneJson.GetNamedNumber(L"height"));
|
||||||
|
CanvasLayoutInfo::Rect zone{ x, y, width, height };
|
||||||
|
info.zones.push_back(zone);
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayoutInfo::GridLayoutInfo(const Minimal& info) :
|
||||||
|
m_rows(info.rows),
|
||||||
|
m_columns(info.columns)
|
||||||
|
{
|
||||||
|
m_rowsPercents.resize(m_rows, 0);
|
||||||
|
m_columnsPercents.resize(m_columns, 0);
|
||||||
|
m_cellChildMap.resize(m_rows, {});
|
||||||
|
for (auto& cellRow : m_cellChildMap)
|
||||||
|
{
|
||||||
|
cellRow.resize(m_columns, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayoutInfo::GridLayoutInfo(const Full& info) :
|
||||||
|
m_rows(info.rows),
|
||||||
|
m_columns(info.columns),
|
||||||
|
m_rowsPercents(info.rowsPercents),
|
||||||
|
m_columnsPercents(info.columnsPercents),
|
||||||
|
m_cellChildMap(info.cellChildMap)
|
||||||
|
{
|
||||||
|
m_rowsPercents.resize(m_rows, 0);
|
||||||
|
m_columnsPercents.resize(m_columns, 0);
|
||||||
|
m_cellChildMap.resize(m_rows, {});
|
||||||
|
for (auto& cellRow : m_cellChildMap)
|
||||||
|
{
|
||||||
|
cellRow.resize(m_columns, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject GridLayoutInfo::ToJson(const GridLayoutInfo& gridInfo)
|
||||||
|
{
|
||||||
|
json::JsonObject infoJson;
|
||||||
|
infoJson.SetNamedValue(L"rows", json::value(gridInfo.m_rows));
|
||||||
|
infoJson.SetNamedValue(L"columns", json::value(gridInfo.m_columns));
|
||||||
|
infoJson.SetNamedValue(L"rows-percentage", NumVecToJsonArray(gridInfo.m_rowsPercents));
|
||||||
|
infoJson.SetNamedValue(L"columns-percentage", NumVecToJsonArray(gridInfo.m_columnsPercents));
|
||||||
|
|
||||||
|
json::JsonArray cellChildMapJson;
|
||||||
|
for (int i = 0; i < gridInfo.m_cellChildMap.size(); ++i)
|
||||||
|
{
|
||||||
|
cellChildMapJson.Append(NumVecToJsonArray(gridInfo.m_cellChildMap[i]));
|
||||||
|
}
|
||||||
|
infoJson.SetNamedValue(L"cell-child-map", cellChildMapJson);
|
||||||
|
|
||||||
|
return infoJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<GridLayoutInfo> GridLayoutInfo::FromJson(const json::JsonObject& infoJson)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GridLayoutInfo info(GridLayoutInfo::Minimal{});
|
||||||
|
|
||||||
|
info.m_rows = static_cast<int>(infoJson.GetNamedNumber(L"rows"));
|
||||||
|
info.m_columns = static_cast<int>(infoJson.GetNamedNumber(L"columns"));
|
||||||
|
|
||||||
|
json::JsonArray rowsPercentage = infoJson.GetNamedArray(L"rows-percentage");
|
||||||
|
json::JsonArray columnsPercentage = infoJson.GetNamedArray(L"columns-percentage");
|
||||||
|
json::JsonArray cellChildMap = infoJson.GetNamedArray(L"cell-child-map");
|
||||||
|
|
||||||
|
if (rowsPercentage.Size() != info.m_rows || columnsPercentage.Size() != info.m_columns || cellChildMap.Size() != info.m_rows)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.m_rowsPercents = JsonArrayToNumVec(rowsPercentage);
|
||||||
|
info.m_columnsPercents = JsonArrayToNumVec(columnsPercentage);
|
||||||
|
for (const auto& cellsRow : cellChildMap)
|
||||||
|
{
|
||||||
|
const auto cellsArray = cellsRow.GetArray();
|
||||||
|
if (cellsArray.Size() != info.m_columns)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
info.cellChildMap().push_back(JsonArrayToNumVec(cellsArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject CustomZoneSetJSON::ToJson(const CustomZoneSetJSON& customZoneSet)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
result.SetNamedValue(L"uuid", json::value(customZoneSet.uuid));
|
||||||
|
result.SetNamedValue(L"name", json::value(customZoneSet.data.name));
|
||||||
|
switch (customZoneSet.data.type)
|
||||||
|
{
|
||||||
|
case CustomLayoutType::Canvas:
|
||||||
|
{
|
||||||
|
result.SetNamedValue(L"type", json::value(L"canvas"));
|
||||||
|
|
||||||
|
CanvasLayoutInfo info = std::get<CanvasLayoutInfo>(customZoneSet.data.info);
|
||||||
|
result.SetNamedValue(L"info", CanvasLayoutInfo::ToJson(info));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CustomLayoutType::Grid:
|
||||||
|
{
|
||||||
|
result.SetNamedValue(L"type", json::value(L"grid"));
|
||||||
|
|
||||||
|
GridLayoutInfo gridInfo = std::get<GridLayoutInfo>(customZoneSet.data.info);
|
||||||
|
result.SetNamedValue(L"info", GridLayoutInfo::ToJson(gridInfo));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CustomZoneSetJSON> CustomZoneSetJSON::FromJson(const json::JsonObject& customZoneSet)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CustomZoneSetJSON result;
|
||||||
|
|
||||||
|
result.uuid = customZoneSet.GetNamedString(L"uuid");
|
||||||
|
result.data.name = customZoneSet.GetNamedString(L"name");
|
||||||
|
|
||||||
|
json::JsonObject infoJson = customZoneSet.GetNamedObject(L"info");
|
||||||
|
std::wstring zoneSetType = std::wstring{ customZoneSet.GetNamedString(L"type") };
|
||||||
|
if (zoneSetType.compare(L"canvas") == 0)
|
||||||
|
{
|
||||||
|
if (auto info = CanvasLayoutInfo::FromJson(infoJson); info.has_value())
|
||||||
|
{
|
||||||
|
result.data.type = CustomLayoutType::Canvas;
|
||||||
|
result.data.info = std::move(info.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (zoneSetType.compare(L"grid") == 0)
|
||||||
|
{
|
||||||
|
if (auto info = GridLayoutInfo::FromJson(infoJson); info.has_value())
|
||||||
|
{
|
||||||
|
result.data.type = CustomLayoutType::Grid;
|
||||||
|
result.data.info = std::move(info.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
240
src/modules/fancyzones/lib/JsonHelpers.h
Normal file
240
src/modules/fancyzones/lib/JsonHelpers.h
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/settings_helpers.h>
|
||||||
|
#include <common/json.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <variant>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include <winnt.h>
|
||||||
|
|
||||||
|
namespace JSONHelpers
|
||||||
|
{
|
||||||
|
constexpr int MAX_ZONE_COUNT = 50;
|
||||||
|
|
||||||
|
enum class ZoneSetLayoutType : int
|
||||||
|
{
|
||||||
|
Blank = -1,
|
||||||
|
Focus,
|
||||||
|
Columns,
|
||||||
|
Rows,
|
||||||
|
Grid,
|
||||||
|
PriorityGrid,
|
||||||
|
Custom
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CustomLayoutType : int
|
||||||
|
{
|
||||||
|
Grid = 0,
|
||||||
|
Canvas
|
||||||
|
};
|
||||||
|
|
||||||
|
std::wstring TypeToString(ZoneSetLayoutType type);
|
||||||
|
ZoneSetLayoutType TypeFromString(const std::wstring& typeStr);
|
||||||
|
|
||||||
|
ZoneSetLayoutType TypeFromLayoutId(int layoutID);
|
||||||
|
|
||||||
|
struct CanvasLayoutInfo
|
||||||
|
{
|
||||||
|
int referenceWidth;
|
||||||
|
int referenceHeight;
|
||||||
|
struct Rect
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
std::vector<CanvasLayoutInfo::Rect> zones;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const CanvasLayoutInfo& canvasInfo);
|
||||||
|
static std::optional<CanvasLayoutInfo> FromJson(const json::JsonObject& infoJson);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GridLayoutInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Minimal
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Full
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int columns;
|
||||||
|
const std::vector<int>& rowsPercents;
|
||||||
|
const std::vector<int>& columnsPercents;
|
||||||
|
const std::vector<std::vector<int>>& cellChildMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
GridLayoutInfo(const Minimal& info);
|
||||||
|
GridLayoutInfo(const Full& info);
|
||||||
|
~GridLayoutInfo() = default;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const GridLayoutInfo& gridInfo);
|
||||||
|
static std::optional<GridLayoutInfo> FromJson(const json::JsonObject& infoJson);
|
||||||
|
|
||||||
|
inline std::vector<int>& rowsPercents() { return m_rowsPercents; };
|
||||||
|
inline std::vector<int>& columnsPercents() { return m_columnsPercents; };
|
||||||
|
inline std::vector<std::vector<int>>& cellChildMap() { return m_cellChildMap; };
|
||||||
|
|
||||||
|
inline int rows() const { return m_rows; }
|
||||||
|
inline int columns() const { return m_columns; }
|
||||||
|
|
||||||
|
inline const std::vector<int>& rowsPercents() const { return m_rowsPercents; };
|
||||||
|
inline const std::vector<int>& columnsPercents() const { return m_columnsPercents; };
|
||||||
|
inline const std::vector<std::vector<int>>& cellChildMap() const { return m_cellChildMap; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_rows;
|
||||||
|
int m_columns;
|
||||||
|
std::vector<int> m_rowsPercents;
|
||||||
|
std::vector<int> m_columnsPercents;
|
||||||
|
std::vector<std::vector<int>> m_cellChildMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CustomZoneSetData
|
||||||
|
{
|
||||||
|
std::wstring name;
|
||||||
|
CustomLayoutType type;
|
||||||
|
std::variant<CanvasLayoutInfo, GridLayoutInfo> info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CustomZoneSetJSON
|
||||||
|
{
|
||||||
|
std::wstring uuid;
|
||||||
|
CustomZoneSetData data;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const CustomZoneSetJSON& device);
|
||||||
|
static std::optional<CustomZoneSetJSON> FromJson(const json::JsonObject& customZoneSet);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(stefan): This needs to be moved to ZoneSet.h (probably)
|
||||||
|
struct ZoneSetData
|
||||||
|
{
|
||||||
|
std::wstring uuid;
|
||||||
|
ZoneSetLayoutType type;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const ZoneSetData& zoneSet);
|
||||||
|
static std::optional<ZoneSetData> FromJson(const json::JsonObject& zoneSet);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppZoneHistoryData
|
||||||
|
{
|
||||||
|
std::wstring zoneSetUuid;
|
||||||
|
std::wstring deviceId;
|
||||||
|
int zoneIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppZoneHistoryJSON
|
||||||
|
{
|
||||||
|
std::wstring appPath;
|
||||||
|
AppZoneHistoryData data;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory);
|
||||||
|
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeviceInfoData
|
||||||
|
{
|
||||||
|
ZoneSetData activeZoneSet;
|
||||||
|
bool showSpacing;
|
||||||
|
int spacing;
|
||||||
|
int zoneCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeviceInfoJSON
|
||||||
|
{
|
||||||
|
std::wstring deviceId;
|
||||||
|
DeviceInfoData data;
|
||||||
|
|
||||||
|
static json::JsonObject ToJson(const DeviceInfoJSON& device);
|
||||||
|
static std::optional<DeviceInfoJSON> FromJson(const json::JsonObject& device);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FancyZonesData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FancyZonesData();
|
||||||
|
|
||||||
|
const std::wstring& GetPersistFancyZonesJSONPath() const;
|
||||||
|
json::JsonObject GetPersistFancyZonesJSON();
|
||||||
|
|
||||||
|
inline const std::unordered_map<std::wstring, DeviceInfoData>& GetDeviceInfoMap() const
|
||||||
|
{
|
||||||
|
return deviceInfoMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::unordered_map<std::wstring, CustomZoneSetData>& GetCustomZoneSetsMap() const
|
||||||
|
{
|
||||||
|
return customZoneSetsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::unordered_map<std::wstring, AppZoneHistoryData>& GetAppZoneHistoryMap() const
|
||||||
|
{
|
||||||
|
return appZoneHistoryMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::wstring GetActiveDeviceId() const
|
||||||
|
{
|
||||||
|
return activeDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetActiveDeviceId(const std::wstring& deviceId)
|
||||||
|
{
|
||||||
|
activeDeviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool DeleteTmpFile(std::wstring_view tmpFilePath) const
|
||||||
|
{
|
||||||
|
return DeleteFileW(tmpFilePath.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDevice(const std::wstring& deviceId);
|
||||||
|
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
|
||||||
|
|
||||||
|
int GetAppLastZoneIndex(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);
|
||||||
|
|
||||||
|
void SetActiveZoneSet(const std::wstring& deviceId, const ZoneSetData& zoneSet);
|
||||||
|
|
||||||
|
void SerializeDeviceInfoToTmpFile(const DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath) const;
|
||||||
|
|
||||||
|
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
|
bool ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
|
bool ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
|
|
||||||
|
bool ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
|
||||||
|
json::JsonArray SerializeAppZoneHistory() const;
|
||||||
|
bool ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
|
||||||
|
json::JsonArray SerializeDeviceInfos() const;
|
||||||
|
bool ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
|
||||||
|
json::JsonArray SerializeCustomZoneSets() const;
|
||||||
|
void CustomZoneSetsToJsonFile(std::wstring_view filePath) const;
|
||||||
|
|
||||||
|
void LoadFancyZonesData();
|
||||||
|
void SaveFancyZonesData() const;
|
||||||
|
|
||||||
|
void MigrateDeviceInfoFromRegistry(const std::wstring& deviceId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void TmpMigrateAppliedZoneSetsFromRegistry();
|
||||||
|
void MigrateCustomZoneSetsFromRegistry();
|
||||||
|
|
||||||
|
std::unordered_map<std::wstring, ZoneSetData> appliedZoneSetsMap{};
|
||||||
|
std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{};
|
||||||
|
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
|
||||||
|
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};
|
||||||
|
|
||||||
|
std::wstring activeDeviceId;
|
||||||
|
std::wstring jsonFilePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
FancyZonesData& FancyZonesDataInstance();
|
||||||
|
}
|
||||||
@@ -7,132 +7,6 @@ namespace RegistryHelpers
|
|||||||
static PCWSTR REG_SETTINGS = L"Software\\SuperFancyZones";
|
static PCWSTR REG_SETTINGS = L"Software\\SuperFancyZones";
|
||||||
static PCWSTR APP_ZONE_HISTORY_SUBKEY = L"AppZoneHistory";
|
static PCWSTR APP_ZONE_HISTORY_SUBKEY = L"AppZoneHistory";
|
||||||
|
|
||||||
inline PCWSTR GetKey(_In_opt_ PCWSTR monitorId, PWSTR key, size_t keyLength)
|
|
||||||
{
|
|
||||||
if (monitorId)
|
|
||||||
{
|
|
||||||
StringCchPrintf(key, keyLength, L"%s\\%s", REG_SETTINGS, monitorId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringCchPrintf(key, keyLength, L"%s", REG_SETTINGS);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HKEY OpenKey(_In_opt_ PCWSTR monitorId)
|
|
||||||
{
|
|
||||||
HKEY hkey;
|
|
||||||
wchar_t key[256];
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
return hkey;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HKEY CreateKey(PCWSTR monitorId)
|
|
||||||
{
|
|
||||||
HKEY hkey;
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, key, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hkey, nullptr) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
return hkey;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline LSTATUS GetAppLastZone(HWND window, PCWSTR appPath, _Out_ PINT iZoneIndex)
|
|
||||||
{
|
|
||||||
*iZoneIndex = -1;
|
|
||||||
|
|
||||||
LSTATUS res{};
|
|
||||||
if (auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
|
||||||
{
|
|
||||||
wchar_t keyPath[256]{};
|
|
||||||
StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"%s\\%s\\%x", REG_SETTINGS, APP_ZONE_HISTORY_SUBKEY, monitor);
|
|
||||||
|
|
||||||
DWORD zoneIndex;
|
|
||||||
DWORD dataType = REG_DWORD;
|
|
||||||
DWORD dataSize = sizeof(DWORD);
|
|
||||||
res = SHRegGetUSValueW(keyPath, appPath, &dataType, &zoneIndex, &dataSize, FALSE, nullptr, 0);
|
|
||||||
if (res == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
*iZoneIndex = static_cast<INT>(zoneIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass -1 for the zoneIndex to delete the entry from the registry
|
|
||||||
inline void SaveAppLastZone(HWND window, PCWSTR appPath, DWORD zoneIndex)
|
|
||||||
{
|
|
||||||
LSTATUS res{};
|
|
||||||
if (auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
|
||||||
{
|
|
||||||
wchar_t keyPath[256]{};
|
|
||||||
StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"%s\\%s\\%x", REG_SETTINGS, APP_ZONE_HISTORY_SUBKEY, monitor);
|
|
||||||
if (zoneIndex == -1)
|
|
||||||
{
|
|
||||||
SHDeleteValueW(HKEY_CURRENT_USER, keyPath, appPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SHRegSetUSValueW(keyPath, appPath, REG_DWORD, &zoneIndex, sizeof(zoneIndex), SHREGSET_FORCE_HKCU);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GetString(PCWSTR uniqueId, PCWSTR setting, PWSTR value, DWORD cbValue)
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(uniqueId, key, ARRAYSIZE(key));
|
|
||||||
SHRegGetUSValueW(key, setting, nullptr, value, &cbValue, FALSE, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetString(PCWSTR uniqueId, PCWSTR setting, PCWSTR value)
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(uniqueId, key, ARRAYSIZE(key));
|
|
||||||
SHRegSetUSValueW(key, setting, REG_SZ, value, sizeof(value) * static_cast<DWORD>(wcslen(value)), SHREGSET_FORCE_HKCU);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename t>
|
|
||||||
inline void GetValue(PCWSTR monitorId, PCWSTR setting, t* value, DWORD size)
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
SHRegGetUSValueW(key, setting, nullptr, value, &size, FALSE, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename t>
|
|
||||||
inline void SetValue(PCWSTR monitorId, PCWSTR setting, t value, DWORD size)
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
SHRegSetUSValueW(key, setting, REG_BINARY, &value, size, SHREGSET_FORCE_HKCU);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void DeleteZoneSet(PCWSTR monitorId, GUID guid)
|
|
||||||
{
|
|
||||||
wil::unique_cotaskmem_string zoneSetId;
|
|
||||||
if (SUCCEEDED_LOG(StringFromCLSID(guid, &zoneSetId)))
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
SHDeleteValueW(HKEY_CURRENT_USER, key, zoneSetId.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void DeleteAllZoneSets(PCWSTR monitorId)
|
|
||||||
{
|
|
||||||
wchar_t key[256]{};
|
|
||||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
|
||||||
SHDeleteKey(HKEY_CURRENT_USER, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HRESULT GetCurrentVirtualDesktop(_Out_ GUID* id)
|
inline HRESULT GetCurrentVirtualDesktop(_Out_ GUID* id)
|
||||||
{
|
{
|
||||||
*id = GUID_NULL;
|
*id = GUID_NULL;
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ struct FancyZonesSettings : winrt::implements<FancyZonesSettings, IFancyZonesSet
|
|||||||
public:
|
public:
|
||||||
FancyZonesSettings(HINSTANCE hinstance, PCWSTR name)
|
FancyZonesSettings(HINSTANCE hinstance, PCWSTR name)
|
||||||
: m_hinstance(hinstance)
|
: m_hinstance(hinstance)
|
||||||
, m_name(name)
|
, m_moduleName(name)
|
||||||
{
|
{
|
||||||
LoadSettings(name, true /*fromFile*/);
|
LoadSettings(name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback = callback; }
|
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback = callback; }
|
||||||
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
|
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
|
||||||
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
|
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
|
||||||
@@ -26,7 +26,7 @@ private:
|
|||||||
|
|
||||||
IFancyZonesCallback* m_callback{};
|
IFancyZonesCallback* m_callback{};
|
||||||
const HINSTANCE m_hinstance;
|
const HINSTANCE m_hinstance;
|
||||||
PCWSTR m_name{};
|
PCWSTR m_moduleName{};
|
||||||
|
|
||||||
Settings m_settings;
|
Settings m_settings;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ private:
|
|||||||
|
|
||||||
IFACEMETHODIMP_(bool) FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_size) noexcept
|
IFACEMETHODIMP_(bool) FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_size) noexcept
|
||||||
{
|
{
|
||||||
PowerToysSettings::Settings settings(m_hinstance, m_name);
|
PowerToysSettings::Settings settings(m_hinstance, m_moduleName);
|
||||||
|
|
||||||
// Pass a string literal or a resource id to Settings::set_description().
|
// Pass a string literal or a resource id to Settings::set_description().
|
||||||
settings.set_description(IDS_SETTING_DESCRIPTION);
|
settings.set_description(IDS_SETTING_DESCRIPTION);
|
||||||
@@ -84,9 +84,9 @@ IFACEMETHODIMP_(bool) FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ in
|
|||||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) FancyZonesSettings::SetConfig(PCWSTR config) noexcept try
|
IFACEMETHODIMP_(void) FancyZonesSettings::SetConfig(PCWSTR serializedPowerToysSettingsJson) noexcept try
|
||||||
{
|
{
|
||||||
LoadSettings(config, false /*fromFile*/);
|
LoadSettings(serializedPowerToysSettingsJson, false /*fromFile*/);
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
if (m_callback)
|
if (m_callback)
|
||||||
{
|
{
|
||||||
@@ -112,7 +112,7 @@ CATCH_LOG();
|
|||||||
void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept try
|
void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept try
|
||||||
{
|
{
|
||||||
PowerToysSettings::PowerToyValues values = fromFile ?
|
PowerToysSettings::PowerToyValues values = fromFile ?
|
||||||
PowerToysSettings::PowerToyValues::load_from_settings_file(m_name) :
|
PowerToysSettings::PowerToyValues::load_from_settings_file(m_moduleName) :
|
||||||
PowerToysSettings::PowerToyValues::from_json_string(config);
|
PowerToysSettings::PowerToyValues::from_json_string(config);
|
||||||
|
|
||||||
for (auto const& setting : m_configBools)
|
for (auto const& setting : m_configBools)
|
||||||
@@ -165,7 +165,7 @@ CATCH_LOG();
|
|||||||
|
|
||||||
void FancyZonesSettings::SaveSettings() noexcept try
|
void FancyZonesSettings::SaveSettings() noexcept try
|
||||||
{
|
{
|
||||||
PowerToysSettings::PowerToyValues values(m_name);
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
|
||||||
for (auto const& setting : m_configBools)
|
for (auto const& setting : m_configBools)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ interface __declspec(uuid("{BA4E77C4-6F44-4C5D-93D3-CBDE880495C2}")) IFancyZones
|
|||||||
{
|
{
|
||||||
IFACEMETHOD_(void, SetCallback)(interface IFancyZonesCallback* callback) = 0;
|
IFACEMETHOD_(void, SetCallback)(interface IFancyZonesCallback* callback) = 0;
|
||||||
IFACEMETHOD_(bool, GetConfig)(_Out_ PWSTR buffer, _Out_ int *buffer_size) = 0;
|
IFACEMETHOD_(bool, GetConfig)(_Out_ PWSTR buffer, _Out_ int *buffer_size) = 0;
|
||||||
IFACEMETHOD_(void, SetConfig)(PCWSTR config) = 0;
|
IFACEMETHOD_(void, SetConfig)(PCWSTR serializedPowerToysSettingsJson) = 0;
|
||||||
IFACEMETHOD_(void, CallCustomAction)(PCWSTR action) = 0;
|
IFACEMETHOD_(void, CallCustomAction)(PCWSTR action) = 0;
|
||||||
IFACEMETHOD_(Settings, GetSettings)() = 0;
|
IFACEMETHOD_(Settings, GetSettings)() = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ void Zone::StampZone(HWND window, bool stamp) noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
winrt::com_ptr<IZone> MakeZone(RECT zoneRect) noexcept
|
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept
|
||||||
{
|
{
|
||||||
return winrt::make_self<Zone>(zoneRect);
|
return winrt::make_self<Zone>(zoneRect);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
|
|||||||
IFACEMETHOD_(size_t, Id)() = 0;
|
IFACEMETHOD_(size_t, Id)() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
winrt::com_ptr<IZone> MakeZone(RECT zoneRect) noexcept;
|
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;
|
||||||
|
|||||||
@@ -1,12 +1,113 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
#include "lib/ZoneSet.h"
|
#include "lib/ZoneSet.h"
|
||||||
#include "lib/RegistryHelpers.h"
|
#include "lib/RegistryHelpers.h"
|
||||||
|
|
||||||
|
#include <common/dpi_aware.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int C_MULTIPLIER = 10000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct GridLayoutInfo {
|
||||||
|
int rows;
|
||||||
|
int columns;
|
||||||
|
int rowsPercents[MAX_ZONE_COUNT];
|
||||||
|
int columnsPercents[MAX_ZONE_COUNT];
|
||||||
|
int cellChildMap[MAX_ZONE_COUNT][MAX_ZONE_COUNT];
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto l = JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Minimal{ .rows = 1, .columns = 1 });
|
||||||
|
// PriorityGrid layout is unique for zoneCount <= 11. For zoneCount > 11 PriorityGrid is same as Grid
|
||||||
|
JSONHelpers::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
|
||||||
|
/* 1 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 1,
|
||||||
|
.columns = 1,
|
||||||
|
.rowsPercents = { 10000 },
|
||||||
|
.columnsPercents = { 10000 },
|
||||||
|
.cellChildMap = { { 0 } } }),
|
||||||
|
/* 2 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 1,
|
||||||
|
.columns = 2,
|
||||||
|
.rowsPercents = { 10000 },
|
||||||
|
.columnsPercents = { 6667, 3333 },
|
||||||
|
.cellChildMap = { { 0, 1 } } }),
|
||||||
|
/* 3 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 1,
|
||||||
|
.columns = 3,
|
||||||
|
.rowsPercents = { 10000 },
|
||||||
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2 } } }),
|
||||||
|
/* 4 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 2,
|
||||||
|
.columns = 3,
|
||||||
|
.rowsPercents = { 5000, 5000 },
|
||||||
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }),
|
||||||
|
/* 5 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 2,
|
||||||
|
.columns = 3,
|
||||||
|
.rowsPercents = { 5000, 5000 },
|
||||||
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }),
|
||||||
|
/* 6 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 3,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }),
|
||||||
|
/* 7 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 3,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }),
|
||||||
|
/* 8 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 4,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 2500, 2500, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }),
|
||||||
|
/* 9 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 4,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 2500, 2500, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }),
|
||||||
|
/* 10 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 4,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 2500, 2500, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }),
|
||||||
|
/* 11 */
|
||||||
|
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
|
||||||
|
.rows = 3,
|
||||||
|
.columns = 4,
|
||||||
|
.rowsPercents = { 3333, 3334, 3333 },
|
||||||
|
.columnsPercents = { 2500, 2500, 2500, 2500 },
|
||||||
|
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 8, 9, 10 } } }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct ZoneSet : winrt::implements<ZoneSet, IZoneSet>
|
struct ZoneSet : winrt::implements<ZoneSet, IZoneSet>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ZoneSet(ZoneSetConfig const& config) : m_config(config)
|
ZoneSet(ZoneSetConfig const& config) :
|
||||||
|
m_config(config)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,18 +117,35 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(GUID) Id() noexcept { return m_config.Id; }
|
IFACEMETHODIMP_(GUID)
|
||||||
IFACEMETHODIMP_(WORD) LayoutId() noexcept { return m_config.LayoutId; }
|
Id() noexcept { return m_config.Id; }
|
||||||
|
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
|
||||||
|
LayoutType() noexcept { return m_config.LayoutType; }
|
||||||
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
|
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
|
||||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneFromPoint(POINT pt) noexcept;
|
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||||
IFACEMETHODIMP_(int) GetZoneIndexFromWindow(HWND window) noexcept;
|
ZoneFromPoint(POINT pt) noexcept;
|
||||||
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>) GetZones() noexcept { return m_zones; }
|
IFACEMETHODIMP_(int)
|
||||||
IFACEMETHODIMP_(void) Save() noexcept;
|
GetZoneIndexFromWindow(HWND window) noexcept;
|
||||||
IFACEMETHODIMP_(void) MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
|
||||||
IFACEMETHODIMP_(void) MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode) noexcept;
|
GetZones() noexcept { return m_zones; }
|
||||||
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, HWND zoneWindow, POINT ptClient) noexcept;
|
IFACEMETHODIMP_(void)
|
||||||
|
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
||||||
|
IFACEMETHODIMP_(void)
|
||||||
|
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode) noexcept;
|
||||||
|
IFACEMETHODIMP_(void)
|
||||||
|
MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient) noexcept;
|
||||||
|
IFACEMETHODIMP_(bool)
|
||||||
|
CalculateZones(MONITORINFO monitorInfo, int zoneCount, int spacing) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool CalculateFocusLayout(Rect workArea, int zoneCount) noexcept;
|
||||||
|
bool CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
|
||||||
|
bool CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
|
||||||
|
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept;
|
||||||
|
bool CalculateCustomLayout(Rect workArea, int spacing) noexcept;
|
||||||
|
|
||||||
|
bool CalculateGridZones(Rect workArea, JSONHelpers::GridLayoutInfo gridLayoutInfo, int spacing);
|
||||||
|
|
||||||
winrt::com_ptr<IZone> ZoneFromWindow(HWND window) noexcept;
|
winrt::com_ptr<IZone> ZoneFromWindow(HWND window) noexcept;
|
||||||
|
|
||||||
std::vector<winrt::com_ptr<IZone>> m_zones;
|
std::vector<winrt::com_ptr<IZone>> m_zones;
|
||||||
@@ -44,7 +162,8 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||||
|
ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
||||||
{
|
{
|
||||||
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
|
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
|
||||||
// To reduce redundant calculations, we will store the last known zones area.
|
// To reduce redundant calculations, we will store the last known zones area.
|
||||||
@@ -61,16 +180,16 @@ IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
|||||||
smallestKnownZone = zone;
|
smallestKnownZone = zone;
|
||||||
|
|
||||||
RECT* r = &smallestKnownZone->GetZoneRect();
|
RECT* r = &smallestKnownZone->GetZoneRect();
|
||||||
smallestKnownZoneArea = (r->right-r->left)*(r->bottom-r->top);
|
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int newZoneArea = (newZoneRect->right-newZoneRect->left)*(newZoneRect->bottom-newZoneRect->top);
|
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top);
|
||||||
|
|
||||||
if (newZoneArea<smallestKnownZoneArea)
|
if (newZoneArea < smallestKnownZoneArea)
|
||||||
{
|
{
|
||||||
smallestKnownZone = zone;
|
smallestKnownZone = zone;
|
||||||
newZoneArea = smallestKnownZoneArea;
|
smallestKnownZoneArea = newZoneArea;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,38 +199,8 @@ IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
|||||||
return smallestKnownZone;
|
return smallestKnownZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) ZoneSet::Save() noexcept
|
IFACEMETHODIMP_(int)
|
||||||
{
|
ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
||||||
size_t const zoneCount = m_zones.size();
|
|
||||||
if (zoneCount == 0)
|
|
||||||
{
|
|
||||||
RegistryHelpers::DeleteZoneSet(m_config.ResolutionKey, m_config.Id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ZoneSetPersistedData data{};
|
|
||||||
data.LayoutId = m_config.LayoutId;
|
|
||||||
data.ZoneCount = static_cast<DWORD>(zoneCount);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
|
||||||
{
|
|
||||||
winrt::com_ptr<IZone> zone = iter->as<IZone>();
|
|
||||||
CopyRect(&data.Zones[i++], &zone->GetZoneRect());
|
|
||||||
}
|
|
||||||
|
|
||||||
wil::unique_cotaskmem_string guid;
|
|
||||||
if (SUCCEEDED_LOG(StringFromCLSID(m_config.Id, &guid)))
|
|
||||||
{
|
|
||||||
if (wil::unique_hkey hkey{ RegistryHelpers::CreateKey(m_config.ResolutionKey) })
|
|
||||||
{
|
|
||||||
RegSetValueExW(hkey.get(), guid.get(), 0, REG_BINARY, reinterpret_cast<BYTE*>(&data), sizeof(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IFACEMETHODIMP_(int) ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
|
||||||
{
|
{
|
||||||
int zoneIndex = 0;
|
int zoneIndex = 0;
|
||||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++, zoneIndex++)
|
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++, zoneIndex++)
|
||||||
@@ -127,26 +216,40 @@ IFACEMETHODIMP_(int) ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
|
IFACEMETHODIMP_(void)
|
||||||
|
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
|
||||||
{
|
{
|
||||||
if (index >= static_cast<int>(m_zones.size()))
|
if (m_zones.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= int(m_zones.size()))
|
||||||
{
|
{
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < m_zones.size())
|
while (auto zoneDrop = ZoneFromWindow(window))
|
||||||
{
|
{
|
||||||
if (auto zone = m_zones.at(index))
|
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||||
{
|
}
|
||||||
zone->AddWindowToZone(window, windowZone, false);
|
|
||||||
}
|
if (auto zone = m_zones.at(index))
|
||||||
|
{
|
||||||
|
zone->AddWindowToZone(window, windowZone, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCode) noexcept
|
IFACEMETHODIMP_(void)
|
||||||
|
ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCode) noexcept
|
||||||
{
|
{
|
||||||
winrt::com_ptr<IZone> oldZone;
|
if (m_zones.empty())
|
||||||
winrt::com_ptr<IZone> newZone;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::com_ptr<IZone> oldZone = nullptr;
|
||||||
|
winrt::com_ptr<IZone> newZone = nullptr;
|
||||||
|
|
||||||
auto iter = std::find(m_zones.begin(), m_zones.end(), ZoneFromWindow(window));
|
auto iter = std::find(m_zones.begin(), m_zones.end(), ZoneFromWindow(window));
|
||||||
if (iter == m_zones.end())
|
if (iter == m_zones.end())
|
||||||
@@ -183,9 +286,10 @@ IFACEMETHODIMP_(void) ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND w
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void) ZoneSet::MoveSizeEnd(HWND window, HWND zoneWindow, POINT ptClient) noexcept
|
IFACEMETHODIMP_(void)
|
||||||
|
ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient) noexcept
|
||||||
{
|
{
|
||||||
if (auto zoneDrop = ZoneFromWindow(window))
|
while (auto zoneDrop = ZoneFromWindow(window))
|
||||||
{
|
{
|
||||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||||
}
|
}
|
||||||
@@ -196,6 +300,314 @@ IFACEMETHODIMP_(void) ZoneSet::MoveSizeEnd(HWND window, HWND zoneWindow, POINT p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(bool)
|
||||||
|
ZoneSet::CalculateZones(MONITORINFO monitorInfo, int zoneCount, int spacing) noexcept
|
||||||
|
{
|
||||||
|
Rect const workArea(monitorInfo.rcWork);
|
||||||
|
//invalid work area
|
||||||
|
if (workArea.width() == 0 || workArea.height() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//invalid zoneCount, may cause division by zero
|
||||||
|
if (zoneCount <= 0 && m_config.LayoutType != JSONHelpers::ZoneSetLayoutType::Custom)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
switch (m_config.LayoutType)
|
||||||
|
{
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::Focus:
|
||||||
|
success = CalculateFocusLayout(workArea, zoneCount);
|
||||||
|
break;
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::Columns:
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::Rows:
|
||||||
|
success = CalculateColumnsAndRowsLayout(workArea, m_config.LayoutType, zoneCount, spacing);
|
||||||
|
break;
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::Grid:
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::PriorityGrid:
|
||||||
|
success = CalculateGridLayout(workArea, m_config.LayoutType, zoneCount, spacing);
|
||||||
|
break;
|
||||||
|
case JSONHelpers::ZoneSetLayoutType::Custom:
|
||||||
|
success = CalculateCustomLayout(workArea, spacing);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateFocusLayout(Rect workArea, int zoneCount) noexcept
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
long left{ long(workArea.width() * 0.1) };
|
||||||
|
long top{ long(workArea.height() * 0.1) };
|
||||||
|
long right{ long(workArea.width() * 0.6) };
|
||||||
|
long bottom{ long(workArea.height() * 0.6) };
|
||||||
|
|
||||||
|
RECT focusZoneRect{ left, top, right, bottom };
|
||||||
|
|
||||||
|
long focusRectXIncrement = (zoneCount <= 1) ? 0 : (int)(workArea.width() * 0.2) / (zoneCount - 1);
|
||||||
|
long focusRectYIncrement = (zoneCount <= 1) ? 0 : (int)(workArea.height() * 0.2) / (zoneCount - 1);
|
||||||
|
|
||||||
|
if (left >= right || top >= bottom || left < 0 || right < 0 || top < 0 || bottom < 0)
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < zoneCount; i++)
|
||||||
|
{
|
||||||
|
AddZone(MakeZone(focusZoneRect));
|
||||||
|
focusZoneRect.left += focusRectXIncrement;
|
||||||
|
focusZoneRect.right += focusRectXIncrement;
|
||||||
|
focusZoneRect.bottom += focusRectYIncrement;
|
||||||
|
focusZoneRect.top += focusRectYIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
int zonePercent = C_MULTIPLIER / zoneCount;
|
||||||
|
|
||||||
|
long totalWidth;
|
||||||
|
long totalHeight;
|
||||||
|
|
||||||
|
long cellWidth;
|
||||||
|
long cellHeight;
|
||||||
|
|
||||||
|
if (type == JSONHelpers::ZoneSetLayoutType::Columns)
|
||||||
|
{
|
||||||
|
totalWidth = workArea.width() - (spacing * (zoneCount + 1));
|
||||||
|
totalHeight = workArea.height() - (spacing * 2);
|
||||||
|
cellWidth = totalWidth * zonePercent / C_MULTIPLIER;
|
||||||
|
cellHeight = totalHeight;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //Rows
|
||||||
|
totalWidth = workArea.width() - (spacing * 2);
|
||||||
|
totalHeight = workArea.height() - (spacing * (zoneCount + 1));
|
||||||
|
cellWidth = totalWidth;
|
||||||
|
cellHeight = totalHeight * zonePercent / C_MULTIPLIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
long top = spacing;
|
||||||
|
long left = spacing;
|
||||||
|
long bottom = top + cellHeight;
|
||||||
|
long right = left + cellWidth;
|
||||||
|
|
||||||
|
for (int zone = 0; zone < zoneCount; zone++)
|
||||||
|
{
|
||||||
|
if (left >= right || top >= bottom || left < 0 || right < 0 || top < 0 || bottom < 0)
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT focusZoneRect{ left, top, right, bottom };
|
||||||
|
AddZone(MakeZone(focusZoneRect));
|
||||||
|
|
||||||
|
if (type == JSONHelpers::ZoneSetLayoutType::Columns)
|
||||||
|
{
|
||||||
|
left += cellWidth + spacing;
|
||||||
|
right = left + cellWidth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //Rows
|
||||||
|
top += cellHeight + spacing;
|
||||||
|
bottom = top + cellHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
|
||||||
|
{
|
||||||
|
const auto count = sizeof(predefinedPriorityGridLayouts) / sizeof(JSONHelpers::GridLayoutInfo);
|
||||||
|
if (type == JSONHelpers::ZoneSetLayoutType::PriorityGrid && zoneCount < count)
|
||||||
|
{
|
||||||
|
return CalculateUniquePriorityGridLayout(workArea, zoneCount, spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rows = 1, columns = 1;
|
||||||
|
while (zoneCount / rows >= rows)
|
||||||
|
{
|
||||||
|
rows++;
|
||||||
|
}
|
||||||
|
rows--;
|
||||||
|
columns = zoneCount / rows;
|
||||||
|
if (zoneCount % rows == 0)
|
||||||
|
{
|
||||||
|
// even grid
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
columns++;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONHelpers::GridLayoutInfo gridLayoutInfo(JSONHelpers::GridLayoutInfo::Minimal{ .rows = rows, .columns = columns });
|
||||||
|
|
||||||
|
for (int row = 0; row < rows; row++)
|
||||||
|
{
|
||||||
|
gridLayoutInfo.rowsPercents()[row] = C_MULTIPLIER / rows;
|
||||||
|
}
|
||||||
|
for (int col = 0; col < columns; col++)
|
||||||
|
{
|
||||||
|
gridLayoutInfo.columnsPercents()[col] = C_MULTIPLIER / columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < rows; ++i)
|
||||||
|
{
|
||||||
|
gridLayoutInfo.cellChildMap()[i] = std::vector<int>(columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (int col = columns - 1; col >= 0; col--)
|
||||||
|
{
|
||||||
|
for (int row = rows - 1; row >= 0; row--)
|
||||||
|
{
|
||||||
|
gridLayoutInfo.cellChildMap()[row][col] = index++;
|
||||||
|
if (index == zoneCount)
|
||||||
|
{
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CalculateGridZones(workArea, gridLayoutInfo, spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept
|
||||||
|
{
|
||||||
|
if (zoneCount <= 0 || zoneCount >= sizeof(predefinedPriorityGridLayouts))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CalculateGridZones(workArea, predefinedPriorityGridLayouts[zoneCount - 1], spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
|
||||||
|
{
|
||||||
|
wil::unique_cotaskmem_string guuidStr;
|
||||||
|
if (SUCCEEDED_LOG(StringFromCLSID(m_config.Id, &guuidStr)))
|
||||||
|
{
|
||||||
|
const std::wstring guuid = guuidStr.get();
|
||||||
|
const auto& customZoneSets = JSONHelpers::FancyZonesDataInstance().GetCustomZoneSetsMap();
|
||||||
|
if (!customZoneSets.contains(guuid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& zoneSet = customZoneSets.at(guuid);
|
||||||
|
if (zoneSet.type == JSONHelpers::CustomLayoutType::Canvas && std::holds_alternative<JSONHelpers::CanvasLayoutInfo>(zoneSet.info))
|
||||||
|
{
|
||||||
|
const auto& zoneSetInfo = std::get<JSONHelpers::CanvasLayoutInfo>(zoneSet.info);
|
||||||
|
for (const auto& zone : zoneSetInfo.zones)
|
||||||
|
{
|
||||||
|
int x = zone.x;
|
||||||
|
int y = zone.y;
|
||||||
|
int width = zone.width;
|
||||||
|
int height = zone.height;
|
||||||
|
|
||||||
|
if (x < 0 || y < 0 || width < 0 || height < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPIAware::Convert(m_config.Monitor, x, y);
|
||||||
|
DPIAware::Convert(m_config.Monitor, width, height);
|
||||||
|
|
||||||
|
AddZone(MakeZone(RECT{ x, y, x + width, y + height }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (zoneSet.type == JSONHelpers::CustomLayoutType::Grid && std::holds_alternative<JSONHelpers::GridLayoutInfo>(zoneSet.info))
|
||||||
|
{
|
||||||
|
const auto& info = std::get<JSONHelpers::GridLayoutInfo>(zoneSet.info);
|
||||||
|
return CalculateGridZones(workArea, info, spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZoneSet::CalculateGridZones(Rect workArea, JSONHelpers::GridLayoutInfo gridLayoutInfo, int spacing)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
long totalWidth = workArea.width() - (spacing * (gridLayoutInfo.columns() + 1));
|
||||||
|
long totalHeight = workArea.height() - (spacing * (gridLayoutInfo.rows() + 1));
|
||||||
|
struct Info
|
||||||
|
{
|
||||||
|
long Extent;
|
||||||
|
long Start;
|
||||||
|
long End;
|
||||||
|
};
|
||||||
|
Info rowInfo[JSONHelpers::MAX_ZONE_COUNT];
|
||||||
|
Info columnInfo[JSONHelpers::MAX_ZONE_COUNT];
|
||||||
|
|
||||||
|
long top = spacing;
|
||||||
|
for (int row = 0; row < gridLayoutInfo.rows(); row++)
|
||||||
|
{
|
||||||
|
rowInfo[row].Start = top;
|
||||||
|
rowInfo[row].Extent = totalHeight * gridLayoutInfo.rowsPercents()[row] / C_MULTIPLIER;
|
||||||
|
rowInfo[row].End = rowInfo[row].Start + rowInfo[row].Extent;
|
||||||
|
top += rowInfo[row].Extent + spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
long left = spacing;
|
||||||
|
for (int col = 0; col < gridLayoutInfo.columns(); col++)
|
||||||
|
{
|
||||||
|
columnInfo[col].Start = left;
|
||||||
|
columnInfo[col].Extent = totalWidth * gridLayoutInfo.columnsPercents()[col] / C_MULTIPLIER;
|
||||||
|
columnInfo[col].End = columnInfo[col].Start + columnInfo[col].Extent;
|
||||||
|
left += columnInfo[col].Extent + spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row = 0; row < gridLayoutInfo.rows(); row++)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < gridLayoutInfo.columns(); col++)
|
||||||
|
{
|
||||||
|
int i = gridLayoutInfo.cellChildMap()[row][col];
|
||||||
|
if (((row == 0) || (gridLayoutInfo.cellChildMap()[row - 1][col] != i)) &&
|
||||||
|
((col == 0) || (gridLayoutInfo.cellChildMap()[row][col - 1] != i)))
|
||||||
|
{
|
||||||
|
left = columnInfo[col].Start;
|
||||||
|
top = rowInfo[row].Start;
|
||||||
|
|
||||||
|
int maxRow = row;
|
||||||
|
while (((maxRow + 1) < gridLayoutInfo.rows()) && (gridLayoutInfo.cellChildMap()[maxRow + 1][col] == i))
|
||||||
|
{
|
||||||
|
maxRow++;
|
||||||
|
}
|
||||||
|
int maxCol = col;
|
||||||
|
while (((maxCol + 1) < gridLayoutInfo.columns()) && (gridLayoutInfo.cellChildMap()[row][maxCol + 1] == i))
|
||||||
|
{
|
||||||
|
maxCol++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long right = columnInfo[maxCol].End;
|
||||||
|
long bottom = rowInfo[maxRow].End;
|
||||||
|
|
||||||
|
if (left >= right || top >= bottom || left < 0 || right < 0 || top < 0 || bottom < 0)
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddZone(MakeZone(RECT{ left, top, right, bottom }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
winrt::com_ptr<IZone> ZoneSet::ZoneFromWindow(HWND window) noexcept
|
winrt::com_ptr<IZone> ZoneSet::ZoneFromWindow(HWND window) noexcept
|
||||||
{
|
{
|
||||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Zone.h"
|
#include "Zone.h"
|
||||||
|
#include "JsonHelpers.h"
|
||||||
|
|
||||||
enum class ZoneSetLayout
|
|
||||||
{
|
|
||||||
Grid,
|
|
||||||
Row,
|
|
||||||
Focus,
|
|
||||||
Custom
|
|
||||||
};
|
|
||||||
|
|
||||||
interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : public IUnknown
|
interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : public IUnknown
|
||||||
{
|
{
|
||||||
IFACEMETHOD_(GUID, Id)() = 0;
|
IFACEMETHOD_(GUID, Id)() = 0;
|
||||||
IFACEMETHOD_(WORD, LayoutId)() = 0;
|
IFACEMETHOD_(JSONHelpers::ZoneSetLayoutType, LayoutType)() = 0;
|
||||||
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
|
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
|
||||||
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
|
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
|
||||||
IFACEMETHOD_(int, GetZoneIndexFromWindow)(HWND window) = 0;
|
IFACEMETHOD_(int, GetZoneIndexFromWindow)(HWND window) = 0;
|
||||||
IFACEMETHOD_(std::vector<winrt::com_ptr<IZone>>, GetZones)() = 0;
|
IFACEMETHOD_(std::vector<winrt::com_ptr<IZone>>, GetZones)() = 0;
|
||||||
IFACEMETHOD_(void, Save)() = 0;
|
|
||||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
|
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
|
||||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, HWND zoneWindow, DWORD vkCode) = 0;
|
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, HWND zoneWindow, DWORD vkCode) = 0;
|
||||||
IFACEMETHOD_(void, MoveSizeEnd)(HWND window, HWND zoneWindow, POINT ptClient) = 0;
|
IFACEMETHOD_(void, MoveWindowIntoZoneByPoint)(HWND window, HWND zoneWindow, POINT ptClient) = 0;
|
||||||
|
IFACEMETHOD_(bool, CalculateZones)(MONITORINFO monitorInfo, int zoneCount, int spacing) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define VERSION_PERSISTEDDATA 0x0000F00D
|
#define VERSION_PERSISTEDDATA 0x0000F00D
|
||||||
@@ -32,28 +26,39 @@ struct ZoneSetPersistedData
|
|||||||
DWORD Version{VERSION_PERSISTEDDATA};
|
DWORD Version{VERSION_PERSISTEDDATA};
|
||||||
WORD LayoutId{};
|
WORD LayoutId{};
|
||||||
DWORD ZoneCount{};
|
DWORD ZoneCount{};
|
||||||
ZoneSetLayout Layout{};
|
JSONHelpers::ZoneSetLayoutType Layout{};
|
||||||
|
RECT Zones[MAX_ZONES]{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZoneSetPersistedDataOLD
|
||||||
|
{
|
||||||
|
static constexpr inline size_t MAX_ZONES = 40;
|
||||||
|
DWORD Version{ VERSION_PERSISTEDDATA };
|
||||||
|
WORD LayoutId{};
|
||||||
|
DWORD ZoneCount{};
|
||||||
|
JSONHelpers::ZoneSetLayoutType Layout{};
|
||||||
DWORD PaddingInner{};
|
DWORD PaddingInner{};
|
||||||
DWORD PaddingOuter{};
|
DWORD PaddingOuter{};
|
||||||
RECT Zones[MAX_ZONES]{};
|
RECT Zones[MAX_ZONES]{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ZoneSetConfig
|
struct ZoneSetConfig
|
||||||
{
|
{
|
||||||
ZoneSetConfig(
|
ZoneSetConfig(
|
||||||
GUID id,
|
GUID id,
|
||||||
WORD layoutId,
|
JSONHelpers::ZoneSetLayoutType layoutType,
|
||||||
HMONITOR monitor,
|
HMONITOR monitor,
|
||||||
PCWSTR resolutionKey) noexcept :
|
PCWSTR resolutionKey) noexcept :
|
||||||
Id(id),
|
Id(id),
|
||||||
LayoutId(layoutId),
|
LayoutType(layoutType),
|
||||||
Monitor(monitor),
|
Monitor(monitor),
|
||||||
ResolutionKey(resolutionKey)
|
ResolutionKey(resolutionKey)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GUID Id{};
|
GUID Id{};
|
||||||
WORD LayoutId{};
|
JSONHelpers::ZoneSetLayoutType LayoutType{};
|
||||||
HMONITOR Monitor{};
|
HMONITOR Monitor{};
|
||||||
PCWSTR ResolutionKey{};
|
PCWSTR ResolutionKey{};
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,14 @@
|
|||||||
#include "FancyZones.h"
|
#include "FancyZones.h"
|
||||||
#include "lib/ZoneSet.h"
|
#include "lib/ZoneSet.h"
|
||||||
|
|
||||||
|
namespace ZoneWindowUtils
|
||||||
|
{
|
||||||
|
const std::wstring& GetActiveZoneSetTmpPath();
|
||||||
|
const std::wstring& GetAppliedZoneSetTmpPath();
|
||||||
|
const std::wstring& GetCustomZoneSetsTmpPath();
|
||||||
|
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId);
|
||||||
|
}
|
||||||
|
|
||||||
interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow : public IUnknown
|
interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow : public IUnknown
|
||||||
{
|
{
|
||||||
IFACEMETHOD(MoveSizeEnter)(HWND window, bool dragEnabled) = 0;
|
IFACEMETHOD(MoveSizeEnter)(HWND window, bool dragEnabled) = 0;
|
||||||
@@ -13,11 +21,10 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
|||||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, DWORD vkCode) = 0;
|
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, DWORD vkCode) = 0;
|
||||||
IFACEMETHOD_(void, CycleActiveZoneSet)(DWORD vkCode) = 0;
|
IFACEMETHOD_(void, CycleActiveZoneSet)(DWORD vkCode) = 0;
|
||||||
IFACEMETHOD_(void, SaveWindowProcessToZoneIndex)(HWND window) = 0;
|
IFACEMETHOD_(void, SaveWindowProcessToZoneIndex)(HWND window) = 0;
|
||||||
IFACEMETHOD_(std::wstring, DeviceId)() = 0;
|
|
||||||
IFACEMETHOD_(std::wstring, UniqueId)() = 0;
|
IFACEMETHOD_(std::wstring, UniqueId)() = 0;
|
||||||
IFACEMETHOD_(std::wstring, WorkAreaKey)() = 0;
|
IFACEMETHOD_(std::wstring, WorkAreaKey)() = 0;
|
||||||
IFACEMETHOD_(IZoneSet*, ActiveZoneSet)() = 0;
|
IFACEMETHOD_(IZoneSet*, ActiveZoneSet)() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
|
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
|
||||||
PCWSTR deviceId, PCWSTR virtualDesktopId, bool flashZones) noexcept;
|
const std::wstring& uniqueId, bool flashZones) noexcept;
|
||||||
|
|||||||
27
src/modules/fancyzones/lib/util.cpp
Normal file
27
src/modules/fancyzones/lib/util.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <common/dpi_aware.h>
|
||||||
|
|
||||||
|
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||||
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||||
|
{
|
||||||
|
UINT dpi{};
|
||||||
|
if (wil::unique_hmodule user32{ LoadLibrary(L"user32.dll") })
|
||||||
|
{
|
||||||
|
if (auto func = reinterpret_cast<GetDpiForMonitorInternalFunc>(GetProcAddress(user32.get(), "GetDpiForMonitorInternal")))
|
||||||
|
{
|
||||||
|
func(monitor, 0, &dpi, &dpi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpi == 0)
|
||||||
|
{
|
||||||
|
if (wil::unique_hdc hdc{ GetDC(nullptr) })
|
||||||
|
{
|
||||||
|
dpi = GetDeviceCaps(hdc.get(), LOGPIXELSX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
|
||||||
|
}
|
||||||
@@ -117,6 +117,12 @@ inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
|||||||
// We're interested in the unique part between the first and last #'s
|
// We're interested in the unique part between the first and last #'s
|
||||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||||
|
const std::wstring defaultDeviceId = L"FallbackDevice";
|
||||||
|
if (!deviceId)
|
||||||
|
{
|
||||||
|
StringCchCopy(parsedId, size, defaultDeviceId.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
wchar_t buffer[256];
|
wchar_t buffer[256];
|
||||||
StringCchCopy(buffer, 256, deviceId);
|
StringCchCopy(buffer, 256, deviceId);
|
||||||
|
|
||||||
@@ -130,12 +136,14 @@ inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringCchCopy(parsedId, size, L"FallbackDevice");
|
StringCchCopy(parsedId, size, defaultDeviceId.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int OpacitySettingToAlpha(int opacity)
|
inline int OpacitySettingToAlpha(int opacity)
|
||||||
{
|
{
|
||||||
// convert percentage to a 0-255 alpha value
|
// convert percentage to a 0-255 alpha value
|
||||||
return opacity * 2.55;
|
return static_cast<int>(opacity * 2.55);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||||
475
src/modules/fancyzones/tests/UnitTests/FancyZones.Spec.cpp
Normal file
475
src/modules/fancyzones/tests/UnitTests/FancyZones.Spec.cpp
Normal file
@@ -0,0 +1,475 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <lib/FancyZones.h>
|
||||||
|
#include <lib/Settings.h>
|
||||||
|
#include <common/common.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
namespace FancyZonesUnitTests
|
||||||
|
{
|
||||||
|
TEST_CLASS(FancyZonesUnitTests)
|
||||||
|
{
|
||||||
|
HINSTANCE m_hInst;
|
||||||
|
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
|
||||||
|
Assert::IsTrue(m_settings != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(Create)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
Assert::IsNotNull(actual.get());
|
||||||
|
}
|
||||||
|
TEST_METHOD(CreateWithEmptyHinstance)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones({}, m_settings);
|
||||||
|
Assert::IsNotNull(actual.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithNullHinstance)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(nullptr, m_settings);
|
||||||
|
Assert::IsNotNull(actual.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithNullSettings)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(m_hInst, nullptr);
|
||||||
|
Assert::IsNull(actual.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(Run)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::atomic<int> counter = 0;
|
||||||
|
const int expectedCount = 10;
|
||||||
|
|
||||||
|
auto runFunc = [&]() {
|
||||||
|
actual->Run();
|
||||||
|
counter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedCount; i++)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread(runFunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& thread : threads)
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::AreEqual(expectedCount, counter.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(Destroy)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::atomic<int> counter = 0;
|
||||||
|
const int expectedCount = 10;
|
||||||
|
|
||||||
|
auto destroyFunc = [&]() {
|
||||||
|
actual->Destroy();
|
||||||
|
counter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedCount; i++)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread(destroyFunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& thread : threads)
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::AreEqual(expectedCount, counter.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RunDestroy)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::atomic<int> counter = 0;
|
||||||
|
const int expectedCount = 20;
|
||||||
|
|
||||||
|
auto func = [&]() {
|
||||||
|
auto idHash = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||||
|
bool run = (idHash % 2 == 0);
|
||||||
|
run ? actual->Run() : actual->Destroy();
|
||||||
|
counter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedCount; i++)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& thread : threads)
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::AreEqual(expectedCount, counter.load());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CLASS(FancyZonesIZoneWindowHostUnitTests)
|
||||||
|
{
|
||||||
|
HINSTANCE m_hInst{};
|
||||||
|
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
|
||||||
|
winrt::com_ptr<IZoneWindowHost> m_zoneWindowHost = nullptr;
|
||||||
|
|
||||||
|
std::wstring serializedPowerToySettings(const Settings& settings)
|
||||||
|
{
|
||||||
|
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
|
||||||
|
|
||||||
|
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
|
||||||
|
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
|
||||||
|
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
|
||||||
|
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
|
||||||
|
|
||||||
|
return ptSettings.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
|
||||||
|
Assert::IsTrue(m_settings != nullptr);
|
||||||
|
|
||||||
|
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
Assert::IsTrue(fancyZones != nullptr);
|
||||||
|
|
||||||
|
m_zoneWindowHost = fancyZones.as<IZoneWindowHost>();
|
||||||
|
Assert::IsTrue(m_zoneWindowHost != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(L"FancyZonesUnitTests") + L"\\settings.json";
|
||||||
|
std::filesystem::remove(settingsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetZoneHighlightColor)
|
||||||
|
{
|
||||||
|
const auto expected = RGB(171, 175, 238);
|
||||||
|
const Settings settings{
|
||||||
|
.shiftDrag = true,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = false,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = true,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#abafee",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app\r\napp2",
|
||||||
|
.excludedAppsArray = { L"APP", L"APP2" },
|
||||||
|
};
|
||||||
|
|
||||||
|
auto config = serializedPowerToySettings(settings);
|
||||||
|
m_settings->SetConfig(config.c_str());
|
||||||
|
|
||||||
|
const auto actual = m_zoneWindowHost->GetZoneHighlightColor();
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetZoneHighlightOpacity)
|
||||||
|
{
|
||||||
|
const auto expected = 88;
|
||||||
|
const Settings settings{
|
||||||
|
.shiftDrag = true,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = false,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = true,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#abafee",
|
||||||
|
.zoneHighlightOpacity = expected,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app\r\napp2",
|
||||||
|
.excludedAppsArray = { L"APP", L"APP2" },
|
||||||
|
};
|
||||||
|
|
||||||
|
auto config = serializedPowerToySettings(settings);
|
||||||
|
m_settings->SetConfig(config.c_str());
|
||||||
|
|
||||||
|
const auto actual = m_zoneWindowHost->GetZoneHighlightOpacity();
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetCurrentMonitorZoneSetEmpty)
|
||||||
|
{
|
||||||
|
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(Mocks::Monitor());
|
||||||
|
Assert::IsNull(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetCurrentMonitorZoneSetNullMonitor)
|
||||||
|
{
|
||||||
|
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(nullptr);
|
||||||
|
Assert::IsNull(actual);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CLASS(FancyZonesIFancyZonesCallbackUnitTests)
|
||||||
|
{
|
||||||
|
HINSTANCE m_hInst{};
|
||||||
|
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
|
||||||
|
winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr;
|
||||||
|
|
||||||
|
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||||
|
|
||||||
|
std::wstring serializedPowerToySettings(const Settings& settings)
|
||||||
|
{
|
||||||
|
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
|
||||||
|
|
||||||
|
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
|
||||||
|
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
|
||||||
|
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
|
||||||
|
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
|
||||||
|
|
||||||
|
return ptSettings.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendKeyboardInput(WORD code, bool release = false)
|
||||||
|
{
|
||||||
|
INPUT ip;
|
||||||
|
ip.type = INPUT_KEYBOARD;
|
||||||
|
ip.ki.wScan = 0; // hardware scan code for key
|
||||||
|
ip.ki.time = 0;
|
||||||
|
ip.ki.dwExtraInfo = 0;
|
||||||
|
ip.ki.wVk = code;
|
||||||
|
ip.ki.dwFlags = release ? KEYEVENTF_KEYUP : 0;
|
||||||
|
SendInput(1, &ip, sizeof(INPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
|
||||||
|
Assert::IsTrue(m_settings != nullptr);
|
||||||
|
|
||||||
|
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||||
|
Assert::IsTrue(fancyZones != nullptr);
|
||||||
|
|
||||||
|
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
|
||||||
|
Assert::IsTrue(m_fzCallback != nullptr);
|
||||||
|
|
||||||
|
m_fancyZonesData = JSONHelpers::FancyZonesData();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_SHIFT, true);
|
||||||
|
sendKeyboardInput(VK_LWIN, true);
|
||||||
|
sendKeyboardInput(VK_CONTROL, true);
|
||||||
|
|
||||||
|
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(L"FancyZonesUnitTests") + L"\\settings.json";
|
||||||
|
std::filesystem::remove(settingsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(InMoveSizeTest)
|
||||||
|
{
|
||||||
|
Assert::IsFalse(m_fzCallback->InMoveSize());
|
||||||
|
|
||||||
|
m_fzCallback->MoveSizeStart(Mocks::Window(), Mocks::Monitor(), POINT{ 0, 0 });
|
||||||
|
Assert::IsFalse(m_fzCallback->InMoveSize()); //point outside of window rect
|
||||||
|
|
||||||
|
const auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
const int paddingX = 8, paddingY = 6;
|
||||||
|
RECT windowRect{};
|
||||||
|
::GetWindowRect(window, &windowRect);
|
||||||
|
m_fzCallback->MoveSizeStart(window, Mocks::Monitor(), POINT{ windowRect.left + paddingX, windowRect.top + paddingY });
|
||||||
|
Assert::IsTrue(m_fzCallback->InMoveSize());
|
||||||
|
|
||||||
|
m_fzCallback->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
|
||||||
|
Assert::IsFalse(m_fzCallback->InMoveSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownNothingPressed)
|
||||||
|
{
|
||||||
|
for (DWORD code = '0'; code <= '9'; code++)
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = code;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownShiftPressed)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_SHIFT);
|
||||||
|
|
||||||
|
for (DWORD code = '0'; code <= '9'; code++)
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = code;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownWinPressed)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_LWIN);
|
||||||
|
|
||||||
|
for (DWORD code = '0'; code <= '9'; code++)
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = code;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownWinShiftPressed)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_LWIN);
|
||||||
|
sendKeyboardInput(VK_SHIFT);
|
||||||
|
|
||||||
|
for (DWORD code = '0'; code <= '9'; code++)
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = code;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownWinCtrlPressed)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_LWIN);
|
||||||
|
sendKeyboardInput(VK_CONTROL);
|
||||||
|
|
||||||
|
const Settings settings{
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto config = serializedPowerToySettings(settings);
|
||||||
|
m_settings->SetConfig(config.c_str());
|
||||||
|
|
||||||
|
for (DWORD code = '0'; code <= '9'; code++)
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = code;
|
||||||
|
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(OnKeyDownWinPressedOverride)
|
||||||
|
{
|
||||||
|
sendKeyboardInput(VK_LWIN);
|
||||||
|
|
||||||
|
const Settings settings{
|
||||||
|
.overrideSnapHotkeys = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto config = serializedPowerToySettings(settings);
|
||||||
|
m_settings->SetConfig(config.c_str());
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_LEFT;
|
||||||
|
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
tagKBDLLHOOKSTRUCT input{};
|
||||||
|
input.vkCode = VK_RIGHT;
|
||||||
|
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,714 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <lib/Settings.h>
|
||||||
|
#include <lib/FancyZones.h>
|
||||||
|
#include <common/settings_helpers.h>
|
||||||
|
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
namespace FancyZonesUnitTests
|
||||||
|
{
|
||||||
|
void compareHotkeyObjects(const PowerToysSettings::HotkeyObject& expected, const PowerToysSettings::HotkeyObject& actual)
|
||||||
|
{
|
||||||
|
Assert::AreEqual(expected.alt_pressed(), actual.alt_pressed());
|
||||||
|
Assert::AreEqual(expected.ctrl_pressed(), actual.ctrl_pressed());
|
||||||
|
Assert::AreEqual(expected.shift_pressed(), actual.shift_pressed());
|
||||||
|
Assert::AreEqual(expected.win_pressed(), actual.win_pressed());
|
||||||
|
|
||||||
|
//NOTE: key_from_code may create different values
|
||||||
|
//Assert::AreEqual(expected.get_key(), actual.get_key());
|
||||||
|
Assert::AreEqual(expected.get_code(), actual.get_code());
|
||||||
|
Assert::AreEqual(expected.get_modifiers(), actual.get_modifiers());
|
||||||
|
Assert::AreEqual(expected.get_modifiers_repeat(), actual.get_modifiers_repeat());
|
||||||
|
}
|
||||||
|
|
||||||
|
void compareSettings(const Settings& expected, const Settings& actual)
|
||||||
|
{
|
||||||
|
Assert::AreEqual(expected.shiftDrag, actual.shiftDrag);
|
||||||
|
Assert::AreEqual(expected.displayChange_moveWindows, actual.displayChange_moveWindows);
|
||||||
|
Assert::AreEqual(expected.virtualDesktopChange_moveWindows, actual.virtualDesktopChange_moveWindows);
|
||||||
|
Assert::AreEqual(expected.zoneSetChange_flashZones, actual.zoneSetChange_flashZones);
|
||||||
|
Assert::AreEqual(expected.zoneSetChange_moveWindows, actual.zoneSetChange_moveWindows);
|
||||||
|
Assert::AreEqual(expected.overrideSnapHotkeys, actual.overrideSnapHotkeys);
|
||||||
|
Assert::AreEqual(expected.appLastZone_moveWindows, actual.appLastZone_moveWindows);
|
||||||
|
Assert::AreEqual(expected.use_cursorpos_editor_startupscreen, actual.use_cursorpos_editor_startupscreen);
|
||||||
|
Assert::AreEqual(expected.zoneHightlightColor.c_str(), actual.zoneHightlightColor.c_str());
|
||||||
|
Assert::AreEqual(expected.zoneHighlightOpacity, actual.zoneHighlightOpacity);
|
||||||
|
Assert::AreEqual(expected.excludedApps.c_str(), actual.excludedApps.c_str());
|
||||||
|
Assert::AreEqual(expected.excludedAppsArray.size(), actual.excludedAppsArray.size());
|
||||||
|
for (int i = 0; i < expected.excludedAppsArray.size(); i++)
|
||||||
|
{
|
||||||
|
Assert::AreEqual(expected.excludedAppsArray[i].c_str(), actual.excludedAppsArray[i].c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
compareHotkeyObjects(expected.editorHotkey, actual.editorHotkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CLASS(FancyZonesSettingsCreationUnitTest)
|
||||||
|
{
|
||||||
|
HINSTANCE m_hInst;
|
||||||
|
PCWSTR m_moduleName = L"FancyZonesTest";
|
||||||
|
std::wstring m_tmpName;
|
||||||
|
|
||||||
|
const PowerToysSettings::HotkeyObject m_defaultHotkeyObject = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_OEM_3);
|
||||||
|
const Settings m_defaultSettings;
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
m_tmpName = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
std::filesystem::remove(m_tmpName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithHinstanceDefault)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZonesSettings({}, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(m_defaultSettings, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithHinstanceNullptr)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZonesSettings(nullptr, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(m_defaultSettings, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithNameEmpty)
|
||||||
|
{
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, L"");
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(m_defaultSettings, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(Create)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithMultipleApps)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app\r\napp1\r\napp2\r\nanother app",
|
||||||
|
.excludedAppsArray = { L"APP", L"APP1", L"APP2", L"ANOTHER APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithBoolValuesMissed)
|
||||||
|
{
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = m_defaultSettings.shiftDrag,
|
||||||
|
.displayChange_moveWindows = m_defaultSettings.displayChange_moveWindows,
|
||||||
|
.virtualDesktopChange_moveWindows = m_defaultSettings.virtualDesktopChange_moveWindows,
|
||||||
|
.zoneSetChange_flashZones = m_defaultSettings.zoneSetChange_flashZones,
|
||||||
|
.zoneSetChange_moveWindows = m_defaultSettings.zoneSetChange_moveWindows,
|
||||||
|
.overrideSnapHotkeys = m_defaultSettings.overrideSnapHotkeys,
|
||||||
|
.appLastZone_moveWindows = m_defaultSettings.appLastZone_moveWindows,
|
||||||
|
.use_cursorpos_editor_startupscreen = m_defaultSettings.use_cursorpos_editor_startupscreen,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateColorMissed)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = m_defaultSettings.zoneHightlightColor,
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateOpacityMissed)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = m_defaultSettings.zoneHighlightOpacity,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateHotkeyMissed)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected = Settings{
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = m_defaultSettings.editorHotkey,
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateAppsMissed)
|
||||||
|
{
|
||||||
|
//prepare data
|
||||||
|
const Settings expected = Settings{
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = m_defaultSettings.excludedApps,
|
||||||
|
.excludedAppsArray = m_defaultSettings.excludedAppsArray,
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(expected, actualSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateWithEmptyJson)
|
||||||
|
{
|
||||||
|
json::to_file(m_tmpName, json::JsonObject());
|
||||||
|
auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(actual != nullptr);
|
||||||
|
|
||||||
|
auto actualSettings = actual->GetSettings();
|
||||||
|
compareSettings(m_defaultSettings, actualSettings);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CLASS(FancyZonesSettingsCallbackUnitTests)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
|
||||||
|
PCWSTR m_moduleName = L"FancyZonesTest";
|
||||||
|
|
||||||
|
struct FZCallback : public winrt::implements<FZCallback, IFancyZonesCallback>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FZCallback(bool* callFlag) :
|
||||||
|
m_callFlag(callFlag)
|
||||||
|
{
|
||||||
|
*m_callFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(bool) InMoveSize() noexcept { return false; }
|
||||||
|
IFACEMETHODIMP_(void) MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept {}
|
||||||
|
IFACEMETHODIMP_(void) MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept {}
|
||||||
|
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept {}
|
||||||
|
IFACEMETHODIMP_(void) VirtualDesktopChanged() noexcept {}
|
||||||
|
IFACEMETHODIMP_(void) VirtualDesktopInitialize() noexcept {}
|
||||||
|
IFACEMETHODIMP_(void) WindowCreated(HWND window) noexcept {}
|
||||||
|
IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept { return false; }
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(void) ToggleEditor() noexcept
|
||||||
|
{
|
||||||
|
Assert::IsNotNull(m_callFlag);
|
||||||
|
*m_callFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(void) SettingsChanged() noexcept
|
||||||
|
{
|
||||||
|
Assert::IsNotNull(m_callFlag);
|
||||||
|
*m_callFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool* m_callFlag = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
HINSTANCE hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
const Settings expected{
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
m_settings = MakeFancyZonesSettings(hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(m_settings != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
|
||||||
|
std::filesystem::remove(settingsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CallbackSetConfig)
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
FZCallback callback(&flag);
|
||||||
|
|
||||||
|
json::JsonObject json{};
|
||||||
|
json.SetNamedValue(L"name", json::JsonValue::CreateStringValue(L"name"));
|
||||||
|
|
||||||
|
m_settings->SetCallback(&callback);
|
||||||
|
m_settings->SetConfig(json.Stringify().c_str());
|
||||||
|
|
||||||
|
Assert::IsTrue(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CallbackCallCustomAction)
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
FZCallback callback(&flag);
|
||||||
|
|
||||||
|
json::JsonObject action{};
|
||||||
|
action.SetNamedValue(L"action_name", json::JsonValue::CreateStringValue(L"ToggledFZEditor"));
|
||||||
|
|
||||||
|
m_settings->SetCallback(&callback);
|
||||||
|
m_settings->CallCustomAction(action.Stringify().c_str());
|
||||||
|
|
||||||
|
Assert::IsTrue(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CallbackCallCustomActionNotToggle)
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
FZCallback callback(&flag);
|
||||||
|
|
||||||
|
json::JsonObject action{};
|
||||||
|
action.SetNamedValue(L"action_name", json::JsonValue::CreateStringValue(L"NOT_ToggledFZEditor"));
|
||||||
|
|
||||||
|
m_settings->SetCallback(&callback);
|
||||||
|
m_settings->CallCustomAction(action.Stringify().c_str());
|
||||||
|
|
||||||
|
Assert::IsFalse(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CallbackGetConfig)
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
FZCallback callback(&flag);
|
||||||
|
|
||||||
|
m_settings->SetCallback(&callback);
|
||||||
|
|
||||||
|
int bufSize = 0;
|
||||||
|
m_settings->GetConfig(L"", &bufSize);
|
||||||
|
|
||||||
|
Assert::IsFalse(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CallbackGetSettings)
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
FZCallback callback(&flag);
|
||||||
|
|
||||||
|
m_settings->SetCallback(&callback);
|
||||||
|
m_settings->GetSettings();
|
||||||
|
|
||||||
|
Assert::IsFalse(flag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CLASS(FancyZonesSettingsUnitTests)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
|
||||||
|
PowerToysSettings::Settings* m_ptSettings = nullptr;
|
||||||
|
PCWSTR m_moduleName = L"FancyZonesTest";
|
||||||
|
|
||||||
|
std::wstring serializedPowerToySettings(const Settings& settings)
|
||||||
|
{
|
||||||
|
PowerToysSettings::Settings ptSettings(HINSTANCE{}, m_moduleName);
|
||||||
|
ptSettings.set_description(IDS_SETTING_DESCRIPTION);
|
||||||
|
ptSettings.set_icon_key(L"pt-fancy-zones");
|
||||||
|
ptSettings.set_overview_link(L"https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/README.md");
|
||||||
|
ptSettings.set_video_link(L"https://youtu.be/rTtGzZYAXgY");
|
||||||
|
|
||||||
|
ptSettings.add_custom_action(
|
||||||
|
L"ToggledFZEditor", // action name.
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_LABEL,
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
|
||||||
|
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
|
||||||
|
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
|
||||||
|
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
|
||||||
|
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
|
||||||
|
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
|
||||||
|
|
||||||
|
return ptSettings.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
|
HINSTANCE hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
|
||||||
|
//init m_settings
|
||||||
|
const Settings expected{
|
||||||
|
.shiftDrag = false,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = true,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = false,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00FFD7",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app",
|
||||||
|
.excludedAppsArray = { L"APP" },
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerToysSettings::PowerToyValues values(m_moduleName);
|
||||||
|
values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag);
|
||||||
|
values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_virtualDesktopChange_moveWindows", expected.virtualDesktopChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones);
|
||||||
|
values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows);
|
||||||
|
values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys);
|
||||||
|
values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows);
|
||||||
|
values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen);
|
||||||
|
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHightlightColor);
|
||||||
|
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
|
||||||
|
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
|
||||||
|
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
|
||||||
|
|
||||||
|
values.save_to_settings_file();
|
||||||
|
|
||||||
|
m_settings = MakeFancyZonesSettings(hInst, m_moduleName);
|
||||||
|
Assert::IsTrue(m_settings != nullptr);
|
||||||
|
|
||||||
|
//init m_ptSettings
|
||||||
|
m_ptSettings = new PowerToysSettings::Settings(hInst, m_moduleName);
|
||||||
|
m_ptSettings->set_description(IDS_SETTING_DESCRIPTION);
|
||||||
|
m_ptSettings->set_icon_key(L"pt-fancy-zones");
|
||||||
|
m_ptSettings->set_overview_link(L"https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/README.md");
|
||||||
|
m_ptSettings->set_video_link(L"https://youtu.be/rTtGzZYAXgY");
|
||||||
|
|
||||||
|
m_ptSettings->add_custom_action(
|
||||||
|
L"ToggledFZEditor", // action name.
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_LABEL,
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
|
||||||
|
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
|
||||||
|
m_ptSettings->add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, expected.editorHotkey);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, expected.shiftDrag);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, expected.overrideSnapHotkeys);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, expected.zoneSetChange_flashZones);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, expected.displayChange_moveWindows);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, expected.zoneSetChange_moveWindows);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, expected.virtualDesktopChange_moveWindows);
|
||||||
|
m_ptSettings->add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, expected.appLastZone_moveWindows);
|
||||||
|
m_ptSettings->add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, expected.use_cursorpos_editor_startupscreen);
|
||||||
|
m_ptSettings->add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, expected.zoneHighlightOpacity, 0, 100, 1);
|
||||||
|
m_ptSettings->add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, expected.zoneHightlightColor);
|
||||||
|
m_ptSettings->add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, expected.excludedApps);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
|
||||||
|
std::filesystem::remove(settingsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetConfig)
|
||||||
|
{
|
||||||
|
const int expectedSize = m_ptSettings->serialize().size() + 1;
|
||||||
|
|
||||||
|
int actualBufferSize = expectedSize;
|
||||||
|
PWSTR actualBuffer = new wchar_t[actualBufferSize];
|
||||||
|
|
||||||
|
Assert::IsTrue(m_settings->GetConfig(actualBuffer, &actualBufferSize));
|
||||||
|
Assert::AreEqual(expectedSize, actualBufferSize);
|
||||||
|
|
||||||
|
Assert::AreEqual(m_ptSettings->serialize().c_str(), actualBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetConfigSmallBuffer)
|
||||||
|
{
|
||||||
|
const auto serialized = m_ptSettings->serialize();
|
||||||
|
const int expectedSize = serialized.size() + 1;
|
||||||
|
|
||||||
|
int actualBufferSize = m_ptSettings->serialize().size() - 1;
|
||||||
|
PWSTR actualBuffer = new wchar_t[actualBufferSize];
|
||||||
|
|
||||||
|
Assert::IsFalse(m_settings->GetConfig(actualBuffer, &actualBufferSize));
|
||||||
|
Assert::AreEqual(expectedSize, actualBufferSize);
|
||||||
|
Assert::AreNotEqual(serialized.c_str(), actualBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetConfigNullBuffer)
|
||||||
|
{
|
||||||
|
const auto serialized = m_ptSettings->serialize();
|
||||||
|
const int expectedSize = serialized.size() + 1;
|
||||||
|
|
||||||
|
int actualBufferSize = 0;
|
||||||
|
PWSTR actualBuffer = nullptr;
|
||||||
|
|
||||||
|
Assert::IsFalse(m_settings->GetConfig(actualBuffer, &actualBufferSize));
|
||||||
|
Assert::AreEqual(expectedSize, actualBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SetConfig)
|
||||||
|
{
|
||||||
|
//cleanup file before call set config
|
||||||
|
const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json";
|
||||||
|
std::filesystem::remove(settingsFile);
|
||||||
|
|
||||||
|
const Settings expected {
|
||||||
|
.shiftDrag = true,
|
||||||
|
.displayChange_moveWindows = true,
|
||||||
|
.virtualDesktopChange_moveWindows = true,
|
||||||
|
.zoneSetChange_flashZones = false,
|
||||||
|
.zoneSetChange_moveWindows = true,
|
||||||
|
.overrideSnapHotkeys = false,
|
||||||
|
.appLastZone_moveWindows = true,
|
||||||
|
.use_cursorpos_editor_startupscreen = true,
|
||||||
|
.zoneHightlightColor = L"#00AABB",
|
||||||
|
.zoneHighlightOpacity = 45,
|
||||||
|
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||||
|
.excludedApps = L"app\r\napp2",
|
||||||
|
.excludedAppsArray = { L"APP", L"APP2" },
|
||||||
|
};
|
||||||
|
|
||||||
|
auto config = serializedPowerToySettings(expected);
|
||||||
|
m_settings->SetConfig(config.c_str());
|
||||||
|
|
||||||
|
auto actual = m_settings->GetSettings();
|
||||||
|
compareSettings(expected, actual);
|
||||||
|
|
||||||
|
Assert::IsTrue(std::filesystem::exists(settingsFile));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
1659
src/modules/fancyzones/tests/UnitTests/JsonHelpers.Tests.cpp
Normal file
1659
src/modules/fancyzones/tests/UnitTests/JsonHelpers.Tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
#include "lib\RegistryHelpers.h"
|
|
||||||
|
|
||||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
|
||||||
|
|
||||||
namespace FancyZonesUnitTests
|
|
||||||
{
|
|
||||||
TEST_CLASS(RegistryHelpersUnitTests){
|
|
||||||
public:
|
|
||||||
TEST_METHOD(GetDefaultKey){
|
|
||||||
// Test the path to the key is the same string.
|
|
||||||
wchar_t key[256];
|
|
||||||
Assert::AreEqual(0, wcscmp(RegistryHelpers::GetKey(nullptr, key, ARRAYSIZE(key)), L"Software\\SuperFancyZones"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_METHOD(GetKeyWithMonitor)
|
|
||||||
{
|
|
||||||
// Test the path to the key is the same string.
|
|
||||||
wchar_t key[256];
|
|
||||||
Assert::AreEqual(0, wcscmp(RegistryHelpers::GetKey(L"Monitor1", key, ARRAYSIZE(key)), L"Software\\SuperFancyZones\\Monitor1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_METHOD(OpenKey)
|
|
||||||
{
|
|
||||||
// The default key should exist.
|
|
||||||
wil::unique_hkey key{ RegistryHelpers::OpenKey({}) };
|
|
||||||
Assert::IsNotNull(key.get());
|
|
||||||
|
|
||||||
// The Monitor1 key shouldn't exist.
|
|
||||||
wil::unique_hkey key2{ RegistryHelpers::OpenKey(L"Monitor1") };
|
|
||||||
Assert::IsNull(key2.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
|
||||||
}
|
|
||||||
@@ -96,12 +96,15 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="FancyZones.Spec.cpp" />
|
||||||
|
<ClCompile Include="FancyZonesSettings.Spec.cpp" />
|
||||||
|
<ClCompile Include="JsonHelpers.Tests.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="RegistryHelpers.Spec.cpp" />
|
|
||||||
<ClCompile Include="Util.Spec.cpp" />
|
<ClCompile Include="Util.Spec.cpp" />
|
||||||
|
<ClCompile Include="Util.cpp" />
|
||||||
<ClCompile Include="Zone.Spec.cpp" />
|
<ClCompile Include="Zone.Spec.cpp" />
|
||||||
<ClCompile Include="ZoneSet.Spec.cpp" />
|
<ClCompile Include="ZoneSet.Spec.cpp" />
|
||||||
<ClCompile Include="ZoneWindow.Spec.cpp" />
|
<ClCompile Include="ZoneWindow.Spec.cpp" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="Source Files">
|
<Filter Include="Source Files">
|
||||||
@@ -24,15 +24,24 @@
|
|||||||
<ClCompile Include="Zone.Spec.cpp">
|
<ClCompile Include="Zone.Spec.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="RegistryHelpers.Spec.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Util.Spec.cpp">
|
<ClCompile Include="Util.Spec.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ZoneWindow.Spec.cpp">
|
<ClCompile Include="ZoneWindow.Spec.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="JsonHelpers.Tests.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Util.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="FancyZonesSettings.Spec.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="FancyZones.Spec.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="pch.h">
|
<ClInclude Include="pch.h">
|
||||||
|
|||||||
149
src/modules/fancyzones/tests/UnitTests/Util.cpp
Normal file
149
src/modules/fancyzones/tests/UnitTests/Util.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
static int s_classId = 0;
|
||||||
|
|
||||||
|
namespace Mocks
|
||||||
|
{
|
||||||
|
class HwndCreator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HwndCreator(const std::wstring& title = L"");
|
||||||
|
|
||||||
|
~HwndCreator();
|
||||||
|
|
||||||
|
HWND operator()(HINSTANCE hInst);
|
||||||
|
|
||||||
|
void setHwnd(HWND val);
|
||||||
|
void setCondition(bool cond);
|
||||||
|
|
||||||
|
inline HINSTANCE getHInstance() const { return m_hInst; }
|
||||||
|
inline const std::wstring& getTitle() const { return m_windowTitle; }
|
||||||
|
inline const std::wstring& getWindowClassName() const { return m_windowClassName; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::wstring m_windowTitle;
|
||||||
|
std::wstring m_windowClassName;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::condition_variable m_conditionVar;
|
||||||
|
bool m_conditionFlag;
|
||||||
|
HANDLE m_thread;
|
||||||
|
|
||||||
|
HINSTANCE m_hInst;
|
||||||
|
HWND m_hWnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
HWND WindowCreate(HINSTANCE hInst)
|
||||||
|
{
|
||||||
|
return HwndCreator()(hInst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK DLLWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (message == WM_DESTROY)
|
||||||
|
{
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL RegisterDLLWindowClass(LPCWSTR szClassName, Mocks::HwndCreator* creator)
|
||||||
|
{
|
||||||
|
if (!creator)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WNDCLASSEX wc;
|
||||||
|
|
||||||
|
wc.hInstance = creator->getHInstance();
|
||||||
|
wc.lpszClassName = szClassName;
|
||||||
|
wc.lpfnWndProc = DLLWindowProc;
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
|
||||||
|
wc.style = CS_DBLCLKS;
|
||||||
|
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||||
|
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
||||||
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||||
|
wc.lpszMenuName = NULL;
|
||||||
|
wc.cbClsExtra = 0;
|
||||||
|
wc.cbWndExtra = 0;
|
||||||
|
wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
|
||||||
|
|
||||||
|
auto regRes = RegisterClassEx(&wc);
|
||||||
|
return regRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI ThreadProc(LPVOID lpParam)
|
||||||
|
{
|
||||||
|
MSG messages;
|
||||||
|
Mocks::HwndCreator* creator = reinterpret_cast<Mocks::HwndCreator*>(lpParam);
|
||||||
|
if (!creator)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (RegisterDLLWindowClass((LPCWSTR)creator->getWindowClassName().c_str(), creator) != 0)
|
||||||
|
{
|
||||||
|
auto hWnd = CreateWindowEx(0, (LPCWSTR)creator->getWindowClassName().c_str(), (LPCWSTR)creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL);
|
||||||
|
creator->setHwnd(hWnd);
|
||||||
|
creator->setCondition(true);
|
||||||
|
|
||||||
|
while (GetMessage(&messages, NULL, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(&messages);
|
||||||
|
DispatchMessage(&messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
creator->setHwnd(hWnd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
creator->setCondition(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Mocks
|
||||||
|
{
|
||||||
|
HwndCreator::HwndCreator(const std::wstring& title) :
|
||||||
|
m_windowTitle(title), m_windowClassName(std::to_wstring(++s_classId)), m_conditionFlag(false), m_thread(nullptr), m_hInst(HINSTANCE{}), m_hWnd(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HwndCreator::~HwndCreator()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
m_conditionVar.wait(lock, [this] { return m_conditionFlag; });
|
||||||
|
|
||||||
|
if (m_thread)
|
||||||
|
{
|
||||||
|
CloseHandle(m_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND HwndCreator::operator()(HINSTANCE hInst)
|
||||||
|
{
|
||||||
|
m_hInst = hInst;
|
||||||
|
m_conditionFlag = false;
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
m_thread = CreateThread(0, NULL, ThreadProc, (LPVOID)this, NULL, NULL);
|
||||||
|
m_conditionVar.wait(lock, [this] { return m_conditionFlag; });
|
||||||
|
|
||||||
|
return m_hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HwndCreator::setHwnd(HWND val)
|
||||||
|
{
|
||||||
|
m_hWnd = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HwndCreator::setCondition(bool cond)
|
||||||
|
{
|
||||||
|
m_conditionFlag = cond;
|
||||||
|
m_conditionVar.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/JsonHelpers.h"
|
||||||
|
|
||||||
namespace CustomAssert
|
namespace CustomAssert
|
||||||
{
|
{
|
||||||
static void AreEqual(const RECT& r1, const RECT& r2)
|
static void AreEqual(const RECT& r1, const RECT& r2)
|
||||||
@@ -13,9 +15,9 @@ namespace CustomAssert
|
|||||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(g1 == g2);
|
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(g1 == g2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AreEqual(WORD w1, WORD w2)
|
static void AreEqual(JSONHelpers::ZoneSetLayoutType t1, JSONHelpers::ZoneSetLayoutType t2)
|
||||||
{
|
{
|
||||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(w1 == w2);
|
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(t1 == t2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,4 +41,5 @@ namespace Mocks
|
|||||||
return reinterpret_cast<HINSTANCE>(++s_nextInstance);
|
return reinterpret_cast<HINSTANCE>(++s_nextInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
HWND WindowCreate(HINSTANCE hInst);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "lib\Zone.h"
|
#include "lib\Zone.h"
|
||||||
|
#include "lib\Settings.h"
|
||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
@@ -7,48 +8,419 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
|||||||
|
|
||||||
namespace FancyZonesUnitTests
|
namespace FancyZonesUnitTests
|
||||||
{
|
{
|
||||||
TEST_CLASS(ZoneUnitTests){
|
TEST_CLASS(ZoneUnitTests)
|
||||||
public:
|
{
|
||||||
TEST_METHOD(TestCreateZone){
|
private:
|
||||||
RECT zoneRect{ 10, 10, 200, 200 };
|
RECT m_zoneRect{ 10, 10, 200, 200 };
|
||||||
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
|
HINSTANCE m_hInst{};
|
||||||
Assert::IsNotNull(&zone);
|
|
||||||
CustomAssert::AreEqual(zoneRect, zone->GetZoneRect());
|
|
||||||
|
|
||||||
constexpr size_t id = 10;
|
HWND addWindow(const winrt::com_ptr<IZone>& zone, bool stamp)
|
||||||
zone->SetId(id);
|
{
|
||||||
Assert::AreEqual(zone->Id(), id);
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
}
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, stamp);
|
||||||
|
|
||||||
TEST_METHOD(ContainsWindow)
|
return window;
|
||||||
{
|
}
|
||||||
RECT zoneRect{ 10, 10, 200, 200 };
|
|
||||||
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
|
|
||||||
HWND newWindow = Mocks::Window();
|
|
||||||
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_METHOD(TestAddRemoveWindow)
|
void addMany(const winrt::com_ptr<IZone>& zone)
|
||||||
{
|
{
|
||||||
RECT zoneRect{ 10, 10, 200, 200 };
|
for (int i = 0; i < 10; i++)
|
||||||
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
|
{
|
||||||
HWND newWindow = Mocks::Window();
|
addWindow(zone, i % 2 == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
zone->AddWindowToZone(newWindow, Mocks::Window(), true);
|
{
|
||||||
Assert::IsTrue(zone->ContainsWindow(newWindow));
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
zone->RemoveWindowFromZone(newWindow, false);
|
public:
|
||||||
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
TEST_METHOD(TestCreateZone)
|
||||||
}
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
Assert::IsNotNull(&zone);
|
||||||
|
CustomAssert::AreEqual(m_zoneRect, zone->GetZoneRect());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_METHOD(TestRemoveInvalidWindow)
|
TEST_METHOD(TestCreateZoneZeroRect)
|
||||||
{
|
{
|
||||||
RECT zoneRect{ 10, 10, 200, 200 };
|
RECT zoneRect{ 0, 0, 0, 0 };
|
||||||
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
|
winrt::com_ptr<IZone> zone = MakeZone(zoneRect);
|
||||||
HWND newWindow = Mocks::Window();
|
Assert::IsNotNull(&zone);
|
||||||
zone->RemoveWindowFromZone(newWindow, false);
|
CustomAssert::AreEqual(zoneRect, zone->GetZoneRect());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
;
|
TEST_METHOD(GetSetId)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
constexpr size_t id = 10;
|
||||||
|
zone->SetId(id);
|
||||||
|
Assert::AreEqual(zone->Id(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsEmpty)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsNonEmptyStampTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
addWindow(zone, true);
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsNonEmptyStampFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
addWindow(zone, false);
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsNonEmptyManyWindows)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsNonEmptyManyZoneWindows)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(IsNonEmptyMany)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
addMany(zone);
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowEmpty)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowNot)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
addMany(zone);
|
||||||
|
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowStampTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND window = addWindow(zone, true);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowStampFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND window = addWindow(zone, false);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowManyWindows)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
std::vector<HWND> windowVec{};
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
windowVec.push_back(window);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wnd : windowVec)
|
||||||
|
{
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(wnd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowManyZoneWindows)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
std::vector<HWND> windowVec{};
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
windowVec.push_back(window);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wnd : windowVec)
|
||||||
|
{
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(wnd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(ContainsWindowMany)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
std::vector<HWND> windowVec{};
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND window = addWindow(zone, i % 2 == 0);
|
||||||
|
windowVec.push_back(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wnd : windowVec)
|
||||||
|
{
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(wnd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(AddWindowNullptr)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = nullptr;
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, true);
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(AddWindowZoneNullptr)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
HWND zoneWindow = nullptr;
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, true);
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(AddManySame)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(AddManySameNullptr)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = nullptr;
|
||||||
|
HWND window = nullptr;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveWindowRestoreSizeTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
|
||||||
|
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(newWindow));
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(newWindow, true);
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveWindowRestoreSizeFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
|
||||||
|
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(newWindow));
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(newWindow, false);
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveInvalidWindowRestoreSizeTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->RemoveWindowFromZone(newWindow, true);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveInvalidWindowRestoreSizeFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
zone->RemoveWindowFromZone(newWindow, false);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveNullptrWindowRestoreSizeTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = nullptr;
|
||||||
|
|
||||||
|
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(newWindow));
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(newWindow, true);
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveNullptrWindowRestoreSizeFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
HWND newWindow = nullptr;
|
||||||
|
|
||||||
|
zone->AddWindowToZone(newWindow, Mocks::WindowCreate(m_hInst), true);
|
||||||
|
Assert::IsFalse(zone->IsEmpty());
|
||||||
|
Assert::IsTrue(zone->ContainsWindow(newWindow));
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(newWindow, false);
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(newWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveMany)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
std::vector<HWND> windowVec{};
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
HWND window = addWindow(zone, i % 2 == 0);
|
||||||
|
windowVec.push_back(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wnd : windowVec)
|
||||||
|
{
|
||||||
|
zone->RemoveWindowFromZone(wnd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveManySame)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(window, true);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
Assert::IsFalse(zone->ContainsWindow(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(RemoveDouble)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND zoneWindow = Mocks::WindowCreate(m_hInst);
|
||||||
|
HWND window = Mocks::WindowCreate(m_hInst);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
zone->AddWindowToZone(window, zoneWindow, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
zone->RemoveWindowFromZone(window, true);
|
||||||
|
zone->RemoveWindowFromZone(window, true);
|
||||||
|
|
||||||
|
Assert::IsTrue(zone->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(StampTrue)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
size_t expected = 123456;
|
||||||
|
zone->SetId(expected);
|
||||||
|
|
||||||
|
HWND window = addWindow(zone, true);
|
||||||
|
|
||||||
|
HANDLE actual = GetProp(window, ZONE_STAMP);
|
||||||
|
Assert::IsNotNull(actual);
|
||||||
|
|
||||||
|
size_t actualVal = HandleToLong(actual);
|
||||||
|
Assert::AreEqual(expected, actualVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(StampTrueNoId)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = addWindow(zone, true);
|
||||||
|
|
||||||
|
HANDLE actual = GetProp(window, ZONE_STAMP);
|
||||||
|
Assert::IsNull(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(StampFalse)
|
||||||
|
{
|
||||||
|
winrt::com_ptr<IZone> zone = MakeZone(m_zoneRect);
|
||||||
|
|
||||||
|
HWND window = addWindow(zone, false);
|
||||||
|
|
||||||
|
HANDLE actual = GetProp(window, ZONE_STAMP);
|
||||||
|
Assert::IsNull(actual);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,14 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <common/common.h>
|
||||||
#include <lib/util.h>
|
#include <lib/util.h>
|
||||||
#include <lib/ZoneSet.h>
|
#include <lib/ZoneSet.h>
|
||||||
#include <lib/ZoneWindow.h>
|
#include <lib/ZoneWindow.h>
|
||||||
|
#include <lib/FancyZones.h>
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
namespace FancyZonesUnitTests
|
namespace FancyZonesUnitTests
|
||||||
@@ -17,10 +22,10 @@ namespace FancyZonesUnitTests
|
|||||||
{
|
{
|
||||||
return RGB(0xFF, 0xFF, 0xFF);
|
return RGB(0xFF, 0xFF, 0xFF);
|
||||||
}
|
}
|
||||||
IFACEMETHODIMP_(GUID)
|
IFACEMETHODIMP_(IZoneWindow*)
|
||||||
GetCurrentMonitorZoneSetId(HMONITOR monitor) noexcept
|
GetParentZoneWindow(HMONITOR monitor) noexcept
|
||||||
{
|
{
|
||||||
return m_guid;
|
return m_zoneWindow;
|
||||||
}
|
}
|
||||||
IFACEMETHODIMP_(int)
|
IFACEMETHODIMP_(int)
|
||||||
GetZoneHighlightOpacity() noexcept
|
GetZoneHighlightOpacity() noexcept
|
||||||
@@ -28,47 +33,636 @@ namespace FancyZonesUnitTests
|
|||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUID m_guid;
|
IZoneWindow* m_zoneWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_CLASS(ZoneWindowUnitTests){
|
TEST_CLASS(ZoneWindowUnitTests)
|
||||||
public:
|
{
|
||||||
|
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
||||||
|
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
|
||||||
|
std::wstringstream m_uniqueId;
|
||||||
|
|
||||||
TEST_METHOD(TestCreateZoneWindow){
|
HINSTANCE m_hInst{};
|
||||||
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(nullptr, Mocks::Instance(), Mocks::Monitor(), L"DeviceId", L"MyVirtualDesktopId", false);
|
HMONITOR m_monitor{};
|
||||||
Assert::IsNotNull(zoneWindow.get());
|
MONITORINFO m_monitorInfo{};
|
||||||
}
|
MockZoneWindowHost m_zoneWindowHost{};
|
||||||
|
IZoneWindowHost* m_hostPtr = m_zoneWindowHost.get_strong().get();
|
||||||
TEST_METHOD(TestDeviceId)
|
|
||||||
{
|
winrt::com_ptr<IZoneWindow> m_zoneWindow;
|
||||||
// Window initialization requires a valid HMONITOR - just use the primary for now.
|
|
||||||
HMONITOR pimaryMonitor = MonitorFromWindow(HWND(), MONITOR_DEFAULTTOPRIMARY);
|
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||||
MockZoneWindowHost host;
|
|
||||||
std::wstring expectedDeviceId = L"SomeRandomValue";
|
std::wstring GuidString(const GUID& guid)
|
||||||
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(dynamic_cast<IZoneWindowHost*>(&host), Mocks::Instance(), pimaryMonitor, expectedDeviceId.c_str(), L"MyVirtualDesktopId", false);
|
{
|
||||||
|
OLECHAR* guidString;
|
||||||
Assert::AreEqual(expectedDeviceId, zoneWindow->DeviceId());
|
Assert::AreEqual(S_OK, StringFromCLSID(guid, &guidString));
|
||||||
}
|
|
||||||
|
std::wstring guidStr{ guidString };
|
||||||
TEST_METHOD(TestUniqueId)
|
CoTaskMemFree(guidString);
|
||||||
{
|
|
||||||
// Unique id of the format "ParsedMonitorDeviceId_MonitorWidth_MonitorHeight_VirtualDesktopId
|
return guidStr;
|
||||||
// Example: "DELA026#5&10a58c63&0&UID16777488_1024_768_MyVirtualDesktopId"
|
}
|
||||||
std::wstring deviceId(L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}");
|
|
||||||
// Window initialization requires a valid HMONITOR - just use the primary for now.
|
std::wstring CreateGuidString()
|
||||||
HMONITOR pimaryMonitor = MonitorFromWindow(HWND(), MONITOR_DEFAULTTOPRIMARY);
|
{
|
||||||
MONITORINFO info;
|
GUID guid;
|
||||||
info.cbSize = sizeof(info);
|
Assert::AreEqual(S_OK, CoCreateGuid(&guid));
|
||||||
Assert::IsTrue(GetMonitorInfo(pimaryMonitor, &info));
|
|
||||||
|
return GuidString(guid);
|
||||||
Rect monitorRect = Rect(info.rcMonitor);
|
}
|
||||||
std::wstringstream ss;
|
|
||||||
ss << L"DELA026#5&10a58c63&0&UID16777488_" << monitorRect.width() << "_" << monitorRect.height() << "_MyVirtualDesktopId";
|
TEST_METHOD_INITIALIZE(Init)
|
||||||
|
{
|
||||||
MockZoneWindowHost host;
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
winrt::com_ptr<IZoneWindow> zoneWindow = MakeZoneWindow(dynamic_cast<IZoneWindowHost*>(&host), Mocks::Instance(), pimaryMonitor, deviceId.c_str(), L"MyVirtualDesktopId", false);
|
|
||||||
Assert::AreEqual(zoneWindow->UniqueId().compare(ss.str()), 0);
|
m_monitor = MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
|
||||||
}
|
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
|
||||||
}
|
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
|
||||||
;
|
|
||||||
|
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_MyVirtualDesktopId";
|
||||||
|
|
||||||
|
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
|
||||||
|
Assert::IsFalse(ZoneWindowUtils::GetAppliedZoneSetTmpPath().empty());
|
||||||
|
Assert::IsFalse(ZoneWindowUtils::GetCustomZoneSetsTmpPath().empty());
|
||||||
|
|
||||||
|
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetActiveZoneSetTmpPath()));
|
||||||
|
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetAppliedZoneSetTmpPath()));
|
||||||
|
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetCustomZoneSetsTmpPath()));
|
||||||
|
|
||||||
|
m_fancyZonesData = JSONHelpers::FancyZonesData();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
|
{
|
||||||
|
//cleanup temp files if were created
|
||||||
|
std::filesystem::remove(ZoneWindowUtils::GetActiveZoneSetTmpPath());
|
||||||
|
std::filesystem::remove(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
|
||||||
|
std::filesystem::remove(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||||
|
|
||||||
|
m_zoneWindow = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::com_ptr<IZoneWindow> InitZoneWindowWithActiveZoneSet()
|
||||||
|
{
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
Assert::IsFalse(std::filesystem::exists(activeZoneSetTempPath));
|
||||||
|
|
||||||
|
const auto type = JSONHelpers::ZoneSetLayoutType::Columns;
|
||||||
|
const auto expectedZoneSet = JSONHelpers::ZoneSetData{ CreateGuidString(), type };
|
||||||
|
const auto data = JSONHelpers::DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
Assert::IsTrue(std::filesystem::exists(activeZoneSetTempPath));
|
||||||
|
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
|
||||||
|
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
|
||||||
|
{
|
||||||
|
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||||
|
|
||||||
|
Assert::IsNotNull(zoneWindow.get());
|
||||||
|
Assert::IsFalse(zoneWindow->IsDragEnabled());
|
||||||
|
Assert::AreEqual(m_uniqueId.str().c_str(), zoneWindow->UniqueId().c_str());
|
||||||
|
Assert::AreEqual(expectedWorkArea, zoneWindow->WorkAreaKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TEST_METHOD(CreateZoneWindow)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
testZoneWindow(m_zoneWindow);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoHinst)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(m_zoneWindow);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true);
|
||||||
|
|
||||||
|
testZoneWindow(m_zoneWindow);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoMonitor)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
Assert::IsNull(m_zoneWindow.get());
|
||||||
|
Assert::IsNotNull(m_hostPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true);
|
||||||
|
|
||||||
|
Assert::IsNull(m_zoneWindow.get());
|
||||||
|
Assert::IsNotNull(m_hostPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoDeviceId)
|
||||||
|
{
|
||||||
|
// Generate unique id without device id
|
||||||
|
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
|
||||||
|
|
||||||
|
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||||
|
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
|
||||||
|
|
||||||
|
Assert::IsNotNull(m_zoneWindow.get());
|
||||||
|
Assert::IsFalse(m_zoneWindow->IsDragEnabled());
|
||||||
|
Assert::AreEqual(expectedUniqueId.c_str(), m_zoneWindow->UniqueId().c_str());
|
||||||
|
Assert::AreEqual(expectedWorkArea, m_zoneWindow->WorkAreaKey());
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowNoDesktopId)
|
||||||
|
{
|
||||||
|
// Generate unique id without virtual desktop id
|
||||||
|
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
|
||||||
|
|
||||||
|
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||||
|
Assert::IsNotNull(m_zoneWindow.get());
|
||||||
|
Assert::IsFalse(m_zoneWindow->IsDragEnabled());
|
||||||
|
Assert::IsTrue(m_zoneWindow->UniqueId().empty());
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowWithActiveZoneTmpFile)
|
||||||
|
{
|
||||||
|
using namespace JSONHelpers;
|
||||||
|
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
|
||||||
|
for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
|
||||||
|
{
|
||||||
|
const auto expectedZoneSet = ZoneSetData{ CreateGuidString(), static_cast<ZoneSetLayoutType>(type) };
|
||||||
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
|
||||||
|
//temp file read on initialization
|
||||||
|
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(actual);
|
||||||
|
|
||||||
|
Assert::IsNotNull(actual->ActiveZoneSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneTmpFile)
|
||||||
|
{
|
||||||
|
using namespace JSONHelpers;
|
||||||
|
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
|
||||||
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
|
const auto expectedZoneSet = ZoneSetData{ CreateGuidString(), type };
|
||||||
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
|
||||||
|
//temp file read on initialization
|
||||||
|
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(actual);
|
||||||
|
|
||||||
|
//custom zone needs temp file for applied zone
|
||||||
|
Assert::IsNotNull(actual->ActiveZoneSet());
|
||||||
|
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
|
||||||
|
Assert::AreEqual((size_t)0, actualZoneSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFile)
|
||||||
|
{
|
||||||
|
using namespace JSONHelpers;
|
||||||
|
|
||||||
|
//save required data
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
|
||||||
|
|
||||||
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
|
const auto customSetGuid = CreateGuidString();
|
||||||
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
|
||||||
|
const auto info = CanvasLayoutInfo{
|
||||||
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
|
};
|
||||||
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
|
auto customZoneJson = CustomZoneSetJSON::ToJson(CustomZoneSetJSON{ customSetGuid, customZoneData });
|
||||||
|
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
|
//temp file read on initialization
|
||||||
|
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(actual);
|
||||||
|
|
||||||
|
//custom zone needs temp file for applied zone
|
||||||
|
Assert::IsNotNull(actual->ActiveZoneSet());
|
||||||
|
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
|
||||||
|
Assert::AreEqual((size_t)1, actualZoneSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithDeletedCustomZones)
|
||||||
|
{
|
||||||
|
using namespace JSONHelpers;
|
||||||
|
|
||||||
|
//save required data
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
|
||||||
|
const auto deletedZonesTempPath = ZoneWindowUtils::GetCustomZoneSetsTmpPath();
|
||||||
|
|
||||||
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
|
const auto customSetGuid = CreateGuidString();
|
||||||
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
|
||||||
|
const auto info = CanvasLayoutInfo{
|
||||||
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
|
};
|
||||||
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
|
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData };
|
||||||
|
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet);
|
||||||
|
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
||||||
|
|
||||||
|
//save same zone as deleted
|
||||||
|
json::JsonObject deletedCustomZoneSets = {};
|
||||||
|
json::JsonArray zonesArray{};
|
||||||
|
zonesArray.Append(json::JsonValue::CreateStringValue(customZoneSet.uuid.substr(1, customZoneSet.uuid.size() - 2).c_str()));
|
||||||
|
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
|
||||||
|
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
|
||||||
|
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
||||||
|
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
|
//temp file read on initialization
|
||||||
|
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(actual);
|
||||||
|
|
||||||
|
Assert::IsNotNull(actual->ActiveZoneSet());
|
||||||
|
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
|
||||||
|
Assert::AreEqual((size_t)1, actualZoneSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithUnusedDeletedCustomZones)
|
||||||
|
{
|
||||||
|
using namespace JSONHelpers;
|
||||||
|
|
||||||
|
//save required data
|
||||||
|
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath();
|
||||||
|
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath();
|
||||||
|
const auto deletedZonesTempPath = ZoneWindowUtils::GetCustomZoneSetsTmpPath();
|
||||||
|
|
||||||
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
|
const auto customSetGuid = CreateGuidString();
|
||||||
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
|
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data };
|
||||||
|
const auto json = DeviceInfoJSON::ToJson(deviceInfo);
|
||||||
|
json::to_file(activeZoneSetTempPath, json);
|
||||||
|
|
||||||
|
const auto info = CanvasLayoutInfo{
|
||||||
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
|
};
|
||||||
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
|
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData };
|
||||||
|
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet);
|
||||||
|
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
||||||
|
|
||||||
|
//save different zone as deleted
|
||||||
|
json::JsonObject deletedCustomZoneSets = {};
|
||||||
|
json::JsonArray zonesArray{};
|
||||||
|
const auto uuid = CreateGuidString();
|
||||||
|
zonesArray.Append(json::JsonValue::CreateStringValue(uuid.substr(1, uuid.size() - 2).c_str()));
|
||||||
|
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
|
||||||
|
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
|
||||||
|
|
||||||
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
||||||
|
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
|
//temp file read on initialization
|
||||||
|
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
testZoneWindow(actual);
|
||||||
|
|
||||||
|
Assert::IsNotNull(actual->ActiveZoneSet());
|
||||||
|
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
|
||||||
|
Assert::AreEqual((size_t)1, actualZoneSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEnter)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEnterTwice)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = E_INVALIDARG;
|
||||||
|
|
||||||
|
m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), false);
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeUpdate)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
Assert::IsTrue(m_zoneWindow->IsDragEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEnd)
|
||||||
|
{
|
||||||
|
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
|
||||||
|
const auto window = Mocks::Window();
|
||||||
|
zoneWindow->MoveSizeEnter(window, true);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
|
||||||
|
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||||
|
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||||
|
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||||
|
Assert::AreNotEqual(-1, actualZoneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEndWindowNotAdded)
|
||||||
|
{
|
||||||
|
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
|
||||||
|
const auto window = Mocks::Window();
|
||||||
|
zoneWindow->MoveSizeEnter(window, true);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
|
||||||
|
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||||
|
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||||
|
Assert::AreEqual(-1, actualZoneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEndDifferentWindows)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto window = Mocks::Window();
|
||||||
|
m_zoneWindow->MoveSizeEnter(window, true);
|
||||||
|
|
||||||
|
const auto expected = E_INVALIDARG;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEndWindowNotSet)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = E_INVALIDARG;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeEndInvalidPoint)
|
||||||
|
{
|
||||||
|
auto zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
|
||||||
|
const auto window = Mocks::Window();
|
||||||
|
zoneWindow->MoveSizeEnter(window, true);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -1, -1 });
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
|
||||||
|
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||||
|
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||||
|
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||||
|
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveSizeCancel)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
|
||||||
|
const auto expected = S_OK;
|
||||||
|
const auto actual = m_zoneWindow->MoveSizeCancel();
|
||||||
|
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveWindowIntoZoneByIndex)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||||
|
|
||||||
|
const auto actual = m_zoneWindow->ActiveZoneSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveWindowIntoZoneByDirection)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
const auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||||
|
const auto actual = actualAppZoneHistory.begin()->second;
|
||||||
|
Assert::AreEqual(0, actual.zoneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(MoveWindowIntoZoneByDirectionManyTimes)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
const auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
|
||||||
|
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_RIGHT);
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||||
|
const auto actual = actualAppZoneHistory.begin()->second;
|
||||||
|
Assert::AreEqual(2, actual.zoneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
|
||||||
|
{
|
||||||
|
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||||
|
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::IsTrue(actualAppZoneHistory.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SaveWindowProcessToZoneIndexNullptrWindow)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
m_zoneWindow->SaveWindowProcessToZoneIndex(nullptr);
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::IsTrue(actualAppZoneHistory.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SaveWindowProcessToZoneIndexNoWindowAdded)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
|
||||||
|
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
|
||||||
|
|
||||||
|
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::IsTrue(actualAppZoneHistory.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
const auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
const auto processPath = get_process_path(window);
|
||||||
|
const auto deviceId = m_zoneWindow->UniqueId();
|
||||||
|
const auto zoneSetId = m_zoneWindow->ActiveZoneSet()->Id();
|
||||||
|
|
||||||
|
//fill app zone history map
|
||||||
|
Assert::IsTrue(m_fancyZonesData.SetAppLastZone(window, deviceId, GuidString(zoneSetId), 0));
|
||||||
|
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
|
||||||
|
Assert::AreEqual(0, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
|
||||||
|
|
||||||
|
//add zone without window
|
||||||
|
const auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
|
||||||
|
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
|
||||||
|
|
||||||
|
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
|
||||||
|
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
|
||||||
|
Assert::AreEqual(0, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(SaveWindowProcessToZoneIndexWindowAdded)
|
||||||
|
{
|
||||||
|
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||||
|
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||||
|
|
||||||
|
auto window = Mocks::WindowCreate(m_hInst);
|
||||||
|
const auto processPath = get_process_path(window);
|
||||||
|
const auto deviceId = m_zoneWindow->UniqueId();
|
||||||
|
const auto zoneSetId = m_zoneWindow->ActiveZoneSet()->Id();
|
||||||
|
|
||||||
|
auto zone = MakeZone(RECT{ 0, 0, 100, 100 });
|
||||||
|
zone->AddWindowToZone(window, Mocks::Window(), false);
|
||||||
|
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
|
||||||
|
|
||||||
|
//fill app zone history map
|
||||||
|
Assert::IsTrue(m_fancyZonesData.SetAppLastZone(window, deviceId, GuidString(zoneSetId), 2));
|
||||||
|
Assert::AreEqual((size_t)1, m_fancyZonesData.GetAppZoneHistoryMap().size());
|
||||||
|
Assert::AreEqual(2, m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex);
|
||||||
|
|
||||||
|
m_zoneWindow->SaveWindowProcessToZoneIndex(window);
|
||||||
|
|
||||||
|
const auto actualAppZoneHistory = m_fancyZonesData.GetAppZoneHistoryMap();
|
||||||
|
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||||
|
const auto expected = m_zoneWindow->ActiveZoneSet()->GetZoneIndexFromWindow(window);
|
||||||
|
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
|
||||||
|
Assert::AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user