mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-19 22:37:58 +08:00
Merge remote-tracking branch 'origin/main' into dev/snickler/net8-upgrade
This commit is contained in:
commit
723e49b662
23
.github/policies/resourceManagement.yml
vendored
23
.github/policies/resourceManagement.yml
vendored
@ -192,6 +192,29 @@ configuration:
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/helped'
|
||||
isRegex: True
|
||||
- or:
|
||||
- activitySenderHasAssociation:
|
||||
association: Owner
|
||||
- activitySenderHasAssociation:
|
||||
association: Member
|
||||
- activitySenderHasAssociation:
|
||||
association: Collaborator
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
- removeLabel:
|
||||
label: Needs-Team-Response
|
||||
- addLabel:
|
||||
label: Resolution-Helped User
|
||||
- addReply:
|
||||
reply: This issue is now marked as resolved. If you have any follow-up questions, please don't hesitate to ask. You can find out more about PowerToys functionalities in our [end-user documentation](https://aka.ms/powertoy-docs].
|
||||
- closeIssue
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
|
@ -73,4 +73,4 @@
|
||||
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
|
||||
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -24,6 +24,10 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
|
||||
- Implements IComputeRequest
|
||||
- `Compute()` will populate `Result` with the base64 encoding of the byte array passed in the constructor
|
||||
|
||||
### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs)
|
||||
- Implements IComputeRequest
|
||||
- `Compute()` will populate `Result` with the decoded byte array of the base64 string passed in the constructor
|
||||
|
||||
### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs)
|
||||
- Implements IComputeRequest
|
||||
- Uses the [`GUIDGenerator`](#guidgenerator) class to generate or compute the requested GUID
|
||||
|
@ -1,7 +1,7 @@
|
||||
# UI Architecture
|
||||
|
||||
The UI code is distributed between two projects: [`PowerToys.Settings`](/src/settings-ui/Settings.UI) and [`Settings.UI`](/src/settings-ui/Settings.UI.Library). [`PowerToys.Settings`](/src/settings-ui/Settings.UI) is a Windows App Sdk .net Unpackaged application. It contains the views for base navigation and modules. Parent display window and corresponding code is present in [`MainWindow.xaml.`](/src/settings-ui/Settings.UI/MainWindow.xaml). Fig 1 provides a description of the UI controls hierarchy and each of the controls have been summarized below :
|
||||
- [`ShellPage.xaml`](/src/settings-ui/Settings.UI/Views/ShellPage.xaml) is a WinUI control, consisting of a side navigation panel with an icon for each module. Clicking on a module icon loads the corresponding `setting.json` file and displays the data in the UI.
|
||||
The UI code is distributed between two projects: [`PowerToys.Settings`](/src/settings-ui/Settings.UI) and [`Settings.UI`](/src/settings-ui/Settings.UI.Library). [`PowerToys.Settings`](/src/settings-ui/Settings.UI) is a Windows App Sdk .net Unpackaged application. It contains the views for base navigation and modules. Parent display window and corresponding code is present in [`MainWindow.xaml`](/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml). Fig 1 provides a description of the UI controls hierarchy and each of the controls have been summarized below :
|
||||
- [`ShellPage.xaml`](/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml) is a WinUI control, consisting of a side navigation panel with an icon for each module. Clicking on a module icon loads the corresponding `setting.json` file and displays the data in the UI.
|
||||
|
||||
![Settings UI architecture](/doc/images/settingsv2/ui-architecture.png)
|
||||
**Fig 1: UI Architecture for settingsv2**
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "keyboard_layout_impl.h"
|
||||
#include "shared_constants.h"
|
||||
|
||||
constexpr DWORD numpadOriginBit = 1ull << 31;
|
||||
|
||||
LayoutMap::LayoutMap() :
|
||||
impl(new LayoutMap::LayoutMapImpl())
|
||||
{
|
||||
@ -110,7 +112,6 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
|
||||
keyboardLayoutMap[VK_BACK] = L"Backspace";
|
||||
keyboardLayoutMap[VK_TAB] = L"Tab";
|
||||
keyboardLayoutMap[VK_CLEAR] = L"Clear";
|
||||
keyboardLayoutMap[VK_RETURN] = L"Enter";
|
||||
keyboardLayoutMap[VK_SHIFT] = L"Shift";
|
||||
keyboardLayoutMap[VK_CONTROL] = L"Ctrl";
|
||||
keyboardLayoutMap[VK_MENU] = L"Alt";
|
||||
@ -118,20 +119,36 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
|
||||
keyboardLayoutMap[VK_CAPITAL] = L"Caps Lock";
|
||||
keyboardLayoutMap[VK_ESCAPE] = L"Esc";
|
||||
keyboardLayoutMap[VK_SPACE] = L"Space";
|
||||
|
||||
keyboardLayoutMap[VK_LEFT] = L"Left";
|
||||
keyboardLayoutMap[VK_RIGHT] = L"Right";
|
||||
keyboardLayoutMap[VK_UP] = L"Up";
|
||||
keyboardLayoutMap[VK_DOWN] = L"Down";
|
||||
keyboardLayoutMap[VK_INSERT] = L"Insert";
|
||||
keyboardLayoutMap[VK_DELETE] = L"Delete";
|
||||
keyboardLayoutMap[VK_PRIOR] = L"PgUp";
|
||||
keyboardLayoutMap[VK_NEXT] = L"PgDn";
|
||||
keyboardLayoutMap[VK_END] = L"End";
|
||||
keyboardLayoutMap[VK_HOME] = L"Home";
|
||||
keyboardLayoutMap[VK_LEFT] = L"Left";
|
||||
keyboardLayoutMap[VK_UP] = L"Up";
|
||||
keyboardLayoutMap[VK_RIGHT] = L"Right";
|
||||
keyboardLayoutMap[VK_DOWN] = L"Down";
|
||||
keyboardLayoutMap[VK_END] = L"End";
|
||||
keyboardLayoutMap[VK_RETURN] = L"Enter";
|
||||
|
||||
keyboardLayoutMap[VK_LEFT | numpadOriginBit] = L"Left (Numpad)";
|
||||
keyboardLayoutMap[VK_RIGHT | numpadOriginBit] = L"Right (Numpad)";
|
||||
keyboardLayoutMap[VK_UP | numpadOriginBit] = L"Up (Numpad)";
|
||||
keyboardLayoutMap[VK_DOWN | numpadOriginBit] = L"Down (Numpad)";
|
||||
keyboardLayoutMap[VK_INSERT | numpadOriginBit] = L"Insert (Numpad)";
|
||||
keyboardLayoutMap[VK_DELETE | numpadOriginBit] = L"Delete (Numpad)";
|
||||
keyboardLayoutMap[VK_PRIOR | numpadOriginBit] = L"PgUp (Numpad)";
|
||||
keyboardLayoutMap[VK_NEXT | numpadOriginBit] = L"PgDn (Numpad)";
|
||||
keyboardLayoutMap[VK_HOME | numpadOriginBit] = L"Home (Numpad)";
|
||||
keyboardLayoutMap[VK_END | numpadOriginBit] = L"End (Numpad)";
|
||||
keyboardLayoutMap[VK_RETURN | numpadOriginBit] = L"Enter (Numpad)";
|
||||
keyboardLayoutMap[VK_DIVIDE | numpadOriginBit] = L"/ (Numpad)";
|
||||
|
||||
keyboardLayoutMap[VK_SELECT] = L"Select";
|
||||
keyboardLayoutMap[VK_PRINT] = L"Print";
|
||||
keyboardLayoutMap[VK_EXECUTE] = L"Execute";
|
||||
keyboardLayoutMap[VK_SNAPSHOT] = L"Print Screen";
|
||||
keyboardLayoutMap[VK_INSERT] = L"Insert";
|
||||
keyboardLayoutMap[VK_DELETE] = L"Delete";
|
||||
keyboardLayoutMap[VK_HELP] = L"Help";
|
||||
keyboardLayoutMap[VK_LWIN] = L"Win (Left)";
|
||||
keyboardLayoutMap[VK_RWIN] = L"Win (Right)";
|
||||
@ -275,6 +292,12 @@ std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcu
|
||||
}
|
||||
}
|
||||
|
||||
// Add numpad keys
|
||||
for (auto it = keyboardLayoutMap.rbegin(); it->first & numpadOriginBit; ++it)
|
||||
{
|
||||
keyCodes.push_back(it->first);
|
||||
}
|
||||
|
||||
// Sort the special keys in alphabetical order
|
||||
std::sort(specialKeys.begin(), specialKeys.end(), [&](const DWORD& lhs, const DWORD& rhs) {
|
||||
return keyboardLayoutMap[lhs] < keyboardLayoutMap[rhs];
|
||||
|
@ -26,7 +26,8 @@
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/SettingsObserver.h>
|
||||
#include <FancyZonesLib/trace.h>
|
||||
#include <FancyZonesLib/WindowDrag.h>
|
||||
#include <FancyZonesLib/WindowKeyboardSnap.h>
|
||||
#include <FancyZonesLib/WindowMouseSnap.h>
|
||||
#include <FancyZonesLib/WorkArea.h>
|
||||
|
||||
enum class DisplayChangeType
|
||||
@ -142,10 +143,6 @@ protected:
|
||||
private:
|
||||
void UpdateWorkAreas(bool updateWindowPositions) noexcept;
|
||||
void CycleWindows(bool reverse) noexcept;
|
||||
bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept;
|
||||
bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept;
|
||||
bool OnSnapHotkey(DWORD vkCode) noexcept;
|
||||
bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept;
|
||||
|
||||
void SyncVirtualDesktops() noexcept;
|
||||
|
||||
@ -153,13 +150,11 @@ private:
|
||||
|
||||
bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept;
|
||||
|
||||
void UpdateActiveLayouts() noexcept;
|
||||
void RefreshLayouts() noexcept;
|
||||
bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept;
|
||||
void ApplyQuickLayout(int key) noexcept;
|
||||
void FlashZones() noexcept;
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
|
||||
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
|
||||
HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept;
|
||||
|
||||
virtual void SettingsUpdate(SettingId type) override;
|
||||
@ -167,7 +162,8 @@ private:
|
||||
const HINSTANCE m_hinstance{};
|
||||
|
||||
HWND m_window{};
|
||||
std::unique_ptr<WindowDrag> m_windowDrag{};
|
||||
std::unique_ptr<WindowMouseSnap> m_windowMouseSnapper{};
|
||||
WindowKeyboardSnap m_windowKeyboardSnapper{};
|
||||
MonitorWorkAreaMap m_workAreaHandler;
|
||||
DraggingState m_draggingState;
|
||||
|
||||
@ -288,8 +284,8 @@ FancyZones::VirtualDesktopChanged() noexcept
|
||||
|
||||
void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
|
||||
{
|
||||
m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetAllWorkAreas());
|
||||
if (m_windowDrag)
|
||||
m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaHandler.GetAllWorkAreas());
|
||||
if (m_windowMouseSnapper)
|
||||
{
|
||||
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
|
||||
{
|
||||
@ -298,13 +294,13 @@ void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
|
||||
|
||||
m_draggingState.Enable();
|
||||
m_draggingState.UpdateDraggingState();
|
||||
m_windowDrag->MoveSizeStart(monitor, m_draggingState.IsDragging());
|
||||
m_windowMouseSnapper->MoveSizeStart(monitor, m_draggingState.IsDragging());
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen)
|
||||
{
|
||||
if (m_windowDrag)
|
||||
if (m_windowMouseSnapper)
|
||||
{
|
||||
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
|
||||
{
|
||||
@ -312,17 +308,17 @@ void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen)
|
||||
}
|
||||
|
||||
m_draggingState.UpdateDraggingState();
|
||||
m_windowDrag->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState());
|
||||
m_windowMouseSnapper->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState());
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeEnd()
|
||||
{
|
||||
if (m_windowDrag)
|
||||
if (m_windowMouseSnapper)
|
||||
{
|
||||
m_windowDrag->MoveSizeEnd();
|
||||
m_windowMouseSnapper->MoveSizeEnd();
|
||||
m_draggingState.Disable();
|
||||
m_windowDrag = nullptr;
|
||||
m_windowMouseSnapper = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,7 +335,7 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
|
||||
workArea = workAreas.at(monitor).get();
|
||||
if (workArea)
|
||||
{
|
||||
indexes = workArea->GetWindowZoneIndexes(window);
|
||||
indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -353,7 +349,7 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
|
||||
{
|
||||
if (secondaryWorkArea)
|
||||
{
|
||||
indexes = secondaryWorkArea->GetWindowZoneIndexes(window);
|
||||
indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, secondaryWorkArea->UniqueId(), secondaryWorkArea->GetLayoutId());
|
||||
workArea = secondaryWorkArea.get();
|
||||
if (!indexes.empty())
|
||||
{
|
||||
@ -365,8 +361,8 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
|
||||
|
||||
if (!indexes.empty() && workArea)
|
||||
{
|
||||
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, indexes);
|
||||
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
|
||||
workArea->Snap(window, indexes);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -604,7 +600,40 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
|
||||
if (message == WM_PRIV_SNAP_HOTKEY)
|
||||
{
|
||||
OnSnapHotkey(static_cast<DWORD>(lparam));
|
||||
// We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning
|
||||
auto foregroundWindow = GetForegroundWindow();
|
||||
|
||||
HMONITOR monitor{ nullptr };
|
||||
if (!FancyZonesSettings::settings().spanZonesAcrossMonitors)
|
||||
{
|
||||
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONULL);
|
||||
}
|
||||
|
||||
if (FancyZonesSettings::settings().moveWindowsBasedOnPosition)
|
||||
{
|
||||
auto monitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
RECT windowRect;
|
||||
if (GetWindowRect(foregroundWindow, &windowRect))
|
||||
{
|
||||
// Check whether Alt is used in the shortcut key combination
|
||||
if (GetAsyncKeyState(VK_MENU) & 0x8000)
|
||||
{
|
||||
m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas(), monitors);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error("Error snapping window by keyboard shortcut: failed to get window rect");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered());
|
||||
}
|
||||
}
|
||||
else if (message == WM_PRIV_INIT)
|
||||
{
|
||||
@ -661,7 +690,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE)
|
||||
{
|
||||
AppliedLayouts::instance().LoadData();
|
||||
UpdateActiveLayouts();
|
||||
RefreshLayouts();
|
||||
}
|
||||
else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE)
|
||||
{
|
||||
@ -800,7 +829,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
|
||||
{
|
||||
for (const auto& [window, zones] : windowsToSnap)
|
||||
{
|
||||
workArea->SnapWindow(window, zones, false);
|
||||
workArea->Snap(window, zones, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -813,9 +842,9 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
|
||||
const auto zones = iter->second;
|
||||
const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor);
|
||||
if (workAreaForMonitor && workAreaForMonitor->GetWindowZoneIndexes(window) == zones)
|
||||
if (workAreaForMonitor && AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaForMonitor->UniqueId(), workAreaForMonitor->GetLayoutId()) == zones)
|
||||
{
|
||||
workAreaForMonitor->SnapWindow(window, zones, false);
|
||||
workAreaForMonitor->Snap(window, zones, false);
|
||||
iter = windowsToSnap.erase(iter);
|
||||
}
|
||||
else
|
||||
@ -829,10 +858,10 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
|
||||
{
|
||||
for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
|
||||
{
|
||||
const auto savedIndexes = workArea->GetWindowZoneIndexes(window);
|
||||
const auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
|
||||
if (savedIndexes == zones)
|
||||
{
|
||||
workArea->SnapWindow(window, zones, false);
|
||||
workArea->Snap(window, zones, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -862,257 +891,6 @@ void FancyZones::CycleWindows(bool reverse) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
|
||||
{
|
||||
HMONITOR current = WorkAreaKeyFromWindow(window);
|
||||
|
||||
std::vector<HMONITOR> monitorInfo = GetMonitorsSorted();
|
||||
if (current && monitorInfo.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
|
||||
{
|
||||
// Multi monitor environment.
|
||||
auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current);
|
||||
do
|
||||
{
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(*currMonitorInfo);
|
||||
if (workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */))
|
||||
{
|
||||
// unassign from previous work area
|
||||
for (auto& [_, prevWorkArea] : m_workAreaHandler.GetAllWorkAreas())
|
||||
{
|
||||
if (prevWorkArea && workArea != prevWorkArea.get())
|
||||
{
|
||||
prevWorkArea->UnsnapWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
return true;
|
||||
}
|
||||
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
|
||||
if (vkCode == VK_RIGHT)
|
||||
{
|
||||
currMonitorInfo = std::next(currMonitorInfo);
|
||||
if (currMonitorInfo == std::end(monitorInfo))
|
||||
{
|
||||
currMonitorInfo = std::begin(monitorInfo);
|
||||
}
|
||||
}
|
||||
else if (vkCode == VK_LEFT)
|
||||
{
|
||||
if (currMonitorInfo == std::begin(monitorInfo))
|
||||
{
|
||||
currMonitorInfo = std::end(monitorInfo);
|
||||
}
|
||||
currMonitorInfo = std::prev(currMonitorInfo);
|
||||
}
|
||||
} while (*currMonitorInfo != current);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(current);
|
||||
// Single monitor environment, or combined multi-monitor environment.
|
||||
if (FancyZonesSettings::settings().restoreSize)
|
||||
{
|
||||
bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */);
|
||||
if (!moved)
|
||||
{
|
||||
FancyZonesWindowUtils::RestoreWindowOrigin(window);
|
||||
FancyZonesWindowUtils::RestoreWindowSize(window);
|
||||
}
|
||||
else if (workArea)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */);
|
||||
|
||||
if (moved)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||
{
|
||||
HMONITOR current = WorkAreaKeyFromWindow(window);
|
||||
|
||||
auto allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
|
||||
if (current && allMonitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
|
||||
{
|
||||
// Multi monitor environment.
|
||||
// First, try to stay on the same monitor
|
||||
bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(current));
|
||||
if (success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If that didn't work, extract zones from all other monitors and target one of them
|
||||
std::vector<RECT> zoneRects;
|
||||
std::vector<std::pair<ZoneIndex, WorkArea*>> zoneRectsInfo;
|
||||
RECT currentMonitorRect{ .top = 0, .bottom = -1 };
|
||||
|
||||
for (const auto& [monitor, monitorRect] : allMonitors)
|
||||
{
|
||||
if (monitor == current)
|
||||
{
|
||||
currentMonitorRect = monitorRect;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(monitor);
|
||||
if (workArea)
|
||||
{
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += monitorRect.left;
|
||||
zoneRect.right += monitorRect.left;
|
||||
zoneRect.top += monitorRect.top;
|
||||
zoneRect.bottom += monitorRect.top;
|
||||
|
||||
zoneRects.emplace_back(zoneRect);
|
||||
zoneRectsInfo.emplace_back(zoneId, workArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we can get the windowRect, if not, just quit
|
||||
RECT windowRect;
|
||||
if (!GetWindowRect(window, &windowRect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
if (workArea)
|
||||
{
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx });
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We reached the end of all monitors.
|
||||
// Try again, cycling on all monitors.
|
||||
// First, add zones from the origin monitor to zoneRects
|
||||
// Sanity check: the current monitor is valid
|
||||
if (currentMonitorRect.top <= currentMonitorRect.bottom)
|
||||
{
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(current);
|
||||
if (workArea)
|
||||
{
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += currentMonitorRect.left;
|
||||
zoneRect.right += currentMonitorRect.left;
|
||||
zoneRect.top += currentMonitorRect.top;
|
||||
zoneRect.bottom += currentMonitorRect.top;
|
||||
|
||||
zoneRects.emplace_back(zoneRect);
|
||||
zoneRectsInfo.emplace_back(zoneId, workArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT combinedRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
||||
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
|
||||
if (workArea)
|
||||
{
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx });
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Giving up
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single monitor environment, or combined multi-monitor environment.
|
||||
return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(current));
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
{
|
||||
// We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning
|
||||
auto window = GetForegroundWindow();
|
||||
if (FancyZonesSettings::settings().moveWindowsBasedOnPosition)
|
||||
{
|
||||
return OnSnapHotkeyBasedOnPosition(window, vkCode);
|
||||
}
|
||||
|
||||
return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && OnSnapHotkeyBasedOnZoneNumber(window, vkCode);
|
||||
}
|
||||
|
||||
bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept
|
||||
{
|
||||
// Check whether Alt is used in the shortcut key combination
|
||||
if (GetAsyncKeyState(VK_MENU) & 0x8000)
|
||||
{
|
||||
bool result = workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode);
|
||||
if (result)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result = workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle);
|
||||
if (result)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::SyncVirtualDesktops() noexcept
|
||||
{
|
||||
auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry();
|
||||
@ -1186,13 +964,13 @@ void FancyZones::SettingsUpdate(SettingId id)
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::UpdateActiveLayouts() noexcept
|
||||
void FancyZones::RefreshLayouts() noexcept
|
||||
{
|
||||
for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
|
||||
{
|
||||
if (workArea)
|
||||
{
|
||||
workArea->UpdateActiveZoneSet();
|
||||
workArea->InitLayout();
|
||||
|
||||
if (FancyZonesSettings::settings().zoneSetChange_moveWindows)
|
||||
{
|
||||
@ -1265,7 +1043,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept
|
||||
{
|
||||
AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value());
|
||||
AppliedLayouts::instance().SaveData();
|
||||
UpdateActiveLayouts();
|
||||
RefreshLayouts();
|
||||
FlashZones();
|
||||
}
|
||||
}
|
||||
@ -1284,32 +1062,6 @@ void FancyZones::FlashZones() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
|
||||
{
|
||||
auto monitorInfo = GetRawMonitorData();
|
||||
FancyZonesUtils::OrderMonitors(monitorInfo);
|
||||
std::vector<HMONITOR> output;
|
||||
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
|
||||
{
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
|
||||
const auto& activeWorkAreaMap = m_workAreaHandler.GetAllWorkAreas();
|
||||
for (const auto& [monitor, workArea] : activeWorkAreaMap)
|
||||
{
|
||||
if (workArea && workArea->GetLayout() != nullptr)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
GetMonitorInfo(monitor, &mi);
|
||||
monitorInfo.push_back({ monitor, mi.rcMonitor });
|
||||
}
|
||||
}
|
||||
return monitorInfo;
|
||||
}
|
||||
|
||||
HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept
|
||||
{
|
||||
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
|
||||
|
@ -103,13 +103,14 @@ namespace JsonUtils
|
||||
}
|
||||
|
||||
data.workAreaId = deviceIdOpt.value();
|
||||
data.zoneSetUuid = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID);
|
||||
|
||||
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
|
||||
std::wstring layoutIdStr = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID).c_str();
|
||||
auto layoutIdOpt = FancyZonesUtils::GuidFromString(layoutIdStr);
|
||||
if (!layoutIdOpt.has_value())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
data.layoutId = layoutIdOpt.value();
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -187,7 +188,11 @@ namespace JsonUtils
|
||||
|
||||
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID, jsonIndexSet);
|
||||
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::DeviceID, device);
|
||||
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIdID, json::value(data.zoneSetUuid));
|
||||
auto layoutIdStr = FancyZonesUtils::GuidToString(data.layoutId);
|
||||
if (layoutIdStr)
|
||||
{
|
||||
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIdID, json::value(layoutIdStr.value()));
|
||||
}
|
||||
|
||||
appHistoryArray.Append(desktopData);
|
||||
}
|
||||
@ -334,7 +339,7 @@ void AppZoneHistory::AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::Mo
|
||||
}
|
||||
}
|
||||
|
||||
bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet)
|
||||
bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId, const ZoneIndexSet& zoneIndexSet)
|
||||
{
|
||||
if (IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
|
||||
{
|
||||
@ -347,8 +352,12 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId);
|
||||
|
||||
auto layoutIdStr = FancyZonesUtils::GuidToString(layoutId);
|
||||
if (layoutIdStr)
|
||||
{
|
||||
Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), layoutIdStr.value());
|
||||
}
|
||||
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
|
||||
@ -362,7 +371,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
|
||||
{
|
||||
// application already has history on this work area, update it with new window position
|
||||
data.processIdToHandleMap[processId] = window;
|
||||
data.zoneSetUuid = zoneSetId;
|
||||
data.layoutId = layoutId;
|
||||
data.zoneIndexSet = zoneIndexSet;
|
||||
SaveData();
|
||||
return true;
|
||||
@ -373,7 +382,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
|
||||
std::unordered_map<DWORD, HWND> processIdToHandleMap{};
|
||||
processIdToHandleMap[processId] = window;
|
||||
FancyZonesDataTypes::AppZoneHistoryData data{ .processIdToHandleMap = processIdToHandleMap,
|
||||
.zoneSetUuid = zoneSetId,
|
||||
.layoutId = layoutId,
|
||||
.workAreaId = workAreaId,
|
||||
.zoneIndexSet = zoneIndexSet };
|
||||
|
||||
@ -392,56 +401,66 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId)
|
||||
bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId)
|
||||
{
|
||||
Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId);
|
||||
|
||||
auto processPath = get_process_path_waiting_uwp(window);
|
||||
if (!processPath.empty())
|
||||
if (processPath.empty())
|
||||
{
|
||||
auto history = m_history.find(processPath);
|
||||
if (history != std::end(m_history))
|
||||
{
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
|
||||
{
|
||||
if (data->workAreaId == workAreaId && data->zoneSetUuid == zoneSetId)
|
||||
{
|
||||
if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
|
||||
{
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
|
||||
data->processIdToHandleMap.erase(processId);
|
||||
}
|
||||
|
||||
// if there is another instance of same application placed in the same zone don't erase history
|
||||
auto windowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
|
||||
for (auto placedWindow : data->processIdToHandleMap)
|
||||
{
|
||||
auto placedWindowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(placedWindow.second);
|
||||
if (IsWindow(placedWindow.second) && (windowZoneStamps == placedWindowZoneStamps))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data = perDesktopData.erase(data);
|
||||
if (perDesktopData.empty())
|
||||
{
|
||||
m_history.erase(processPath);
|
||||
}
|
||||
SaveData();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto history = m_history.find(processPath);
|
||||
if (history == std::end(m_history))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto layoutIdStrOpt = FancyZonesUtils::GuidToString(layoutId);
|
||||
if (!layoutIdStrOpt)
|
||||
{
|
||||
Logger::error("Invalid layout id");
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), layoutIdStrOpt.value());
|
||||
|
||||
auto& perDesktopData = history->second;
|
||||
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
|
||||
{
|
||||
if (data->workAreaId == workAreaId && data->layoutId == layoutId)
|
||||
{
|
||||
if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
|
||||
{
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
|
||||
data->processIdToHandleMap.erase(processId);
|
||||
}
|
||||
|
||||
// if there is another instance of same application placed in the same zone don't erase history
|
||||
auto windowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
|
||||
for (auto placedWindow : data->processIdToHandleMap)
|
||||
{
|
||||
auto placedWindowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(placedWindow.second);
|
||||
if (IsWindow(placedWindow.second) && (windowZoneStamps == placedWindowZoneStamps))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data = perDesktopData.erase(data);
|
||||
if (perDesktopData.empty())
|
||||
{
|
||||
m_history.erase(processPath);
|
||||
}
|
||||
SaveData();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++data;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -532,7 +551,7 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const
|
||||
ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const
|
||||
{
|
||||
auto processPath = get_process_path_waiting_uwp(window);
|
||||
if (processPath.empty())
|
||||
@ -559,7 +578,7 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
|
||||
const auto& perDesktopData = history->second;
|
||||
for (const auto& data : perDesktopData)
|
||||
{
|
||||
if (data.zoneSetUuid == zoneSetId && data.workAreaId == workAreaId)
|
||||
if (data.layoutId == layoutId && data.workAreaId == workAreaId)
|
||||
{
|
||||
if (data.workAreaId.virtualDesktopId == workAreaId.virtualDesktopId || data.workAreaId.virtualDesktopId == GUID_NULL)
|
||||
{
|
||||
|
@ -45,8 +45,8 @@ public:
|
||||
void SaveData();
|
||||
void AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::MonitorId>& ids);
|
||||
|
||||
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
|
||||
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId);
|
||||
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId, const ZoneIndexSet& zoneIndexSet);
|
||||
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId);
|
||||
|
||||
void RemoveApp(const std::wstring& appPath);
|
||||
|
||||
@ -54,7 +54,7 @@ public:
|
||||
std::optional<FancyZonesDataTypes::AppZoneHistoryData> GetZoneHistory(const std::wstring& appPath, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
|
||||
|
||||
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const;
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const;
|
||||
|
||||
void SyncVirtualDesktops();
|
||||
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);
|
||||
|
@ -144,9 +144,9 @@ namespace FancyZonesDataTypes
|
||||
{
|
||||
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
|
||||
|
||||
std::wstring zoneSetUuid;
|
||||
WorkAreaId workAreaId;
|
||||
ZoneIndexSet zoneIndexSet;
|
||||
GUID layoutId = {};
|
||||
WorkAreaId workAreaId = {};
|
||||
ZoneIndexSet zoneIndexSet = {};
|
||||
};
|
||||
|
||||
struct DeviceInfoData
|
||||
|
@ -71,7 +71,8 @@
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
<ClInclude Include="VirtualDesktop.h" />
|
||||
<ClInclude Include="WindowDrag.h" />
|
||||
<ClInclude Include="WindowKeyboardSnap.h" />
|
||||
<ClInclude Include="WindowMouseSnap.h" />
|
||||
<ClInclude Include="FancyZonesWindowProperties.h" />
|
||||
<ClInclude Include="WindowUtils.h" />
|
||||
<ClInclude Include="Zone.h" />
|
||||
@ -123,7 +124,8 @@
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
<ClCompile Include="VirtualDesktop.cpp" />
|
||||
<ClCompile Include="WindowDrag.cpp" />
|
||||
<ClCompile Include="WindowKeyboardSnap.cpp" />
|
||||
<ClCompile Include="WindowMouseSnap.cpp" />
|
||||
<ClCompile Include="WindowUtils.cpp" />
|
||||
<ClCompile Include="Zone.cpp" />
|
||||
<ClCompile Include="WorkArea.cpp" />
|
||||
|
@ -162,7 +162,10 @@
|
||||
<ClInclude Include="DraggingState.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WindowDrag.h">
|
||||
<ClInclude Include="WindowMouseSnap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WindowKeyboardSnap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@ -263,7 +266,10 @@
|
||||
<ClCompile Include="DraggingState.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WindowDrag.cpp">
|
||||
<ClCompile Include="WindowMouseSnap.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WindowKeyboardSnap.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
@ -15,7 +15,7 @@ namespace ZonedWindowProperties
|
||||
const wchar_t PropertySortKeyWithinZone[] = L"FancyZones_TabSortKeyWithinZone";
|
||||
}
|
||||
|
||||
void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet)
|
||||
bool FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet)
|
||||
{
|
||||
RemoveZoneIndexProperty(window);
|
||||
ZoneIndexSetBitmask bitmask = ZoneIndexSetBitmask::FromIndexSet(zoneSet);
|
||||
@ -33,6 +33,7 @@ void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneI
|
||||
if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone64ID, rawData))
|
||||
{
|
||||
Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,8 +50,11 @@ void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneI
|
||||
if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone128ID, rawData))
|
||||
{
|
||||
Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FancyZonesWindowProperties::RemoveZoneIndexProperty(HWND window)
|
||||
|
@ -18,7 +18,7 @@ namespace ZonedWindowProperties
|
||||
|
||||
namespace FancyZonesWindowProperties
|
||||
{
|
||||
void StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet);
|
||||
bool StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet);
|
||||
void RemoveZoneIndexProperty(HWND window);
|
||||
ZoneIndexSet RetrieveZoneIndexProperty(HWND window);
|
||||
|
||||
|
@ -277,13 +277,16 @@ namespace
|
||||
.monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(deviceId->deviceName) },
|
||||
.virtualDesktopId = deviceId->virtualDesktopId
|
||||
};
|
||||
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
|
||||
|
||||
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
|
||||
std::wstring layoutIdStr = json.GetNamedString(NonLocalizable::ZoneSetUuidStr).c_str();
|
||||
auto layoutIdOpt = FancyZonesUtils::GuidFromString(layoutIdStr);
|
||||
if (!layoutIdOpt.has_value())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
data.layoutId = layoutIdOpt.value();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -6,37 +6,10 @@
|
||||
#include <FancyZonesLib/VirtualDesktop.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
|
||||
LayoutAssignedWindows::LayoutAssignedWindows()
|
||||
{
|
||||
m_extendData = std::make_unique<ExtendWindowModeData>();
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::Assign(HWND window, const ZoneIndexSet& zones)
|
||||
{
|
||||
Dismiss(window);
|
||||
|
||||
// clear info about extension
|
||||
std::erase_if(m_extendData->windowInitialIndexSet, [window](const auto& item) { return item.first == window; });
|
||||
std::erase_if(m_extendData->windowFinalIndex, [window](const auto& item) { return item.first == window; });
|
||||
|
||||
for (const auto& index : zones)
|
||||
{
|
||||
m_windowIndexSet[window].push_back(index);
|
||||
}
|
||||
|
||||
if (FancyZonesSettings::settings().disableRoundCorners)
|
||||
{
|
||||
FancyZonesWindowUtils::DisableRoundCorners(window);
|
||||
}
|
||||
|
||||
auto tabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(window);
|
||||
InsertWindowIntoZone(window, tabSortKeyWithinZone, zones);
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::Extend(HWND window, const ZoneIndexSet& zones)
|
||||
{
|
||||
Dismiss(window);
|
||||
|
||||
|
||||
for (const auto& index : zones)
|
||||
{
|
||||
m_windowIndexSet[window].push_back(index);
|
||||
@ -133,11 +106,6 @@ void LayoutAssignedWindows::CycleWindows(HWND window, bool reverse)
|
||||
}
|
||||
}
|
||||
|
||||
const std::unique_ptr<LayoutAssignedWindows::ExtendWindowModeData>& LayoutAssignedWindows::ExtendWindowData()
|
||||
{
|
||||
return m_extendData;
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet)
|
||||
{
|
||||
if (tabSortKeyWithinZone.has_value())
|
||||
|
@ -4,19 +4,11 @@
|
||||
|
||||
class LayoutAssignedWindows
|
||||
{
|
||||
public:
|
||||
struct ExtendWindowModeData
|
||||
{
|
||||
std::map<HWND, ZoneIndexSet> windowInitialIndexSet;
|
||||
std::map<HWND, ZoneIndex> windowFinalIndex;
|
||||
};
|
||||
|
||||
public :
|
||||
LayoutAssignedWindows();
|
||||
LayoutAssignedWindows() = default;
|
||||
~LayoutAssignedWindows() = default;
|
||||
|
||||
void Assign(HWND window, const ZoneIndexSet& zones);
|
||||
void Extend(HWND window, const ZoneIndexSet& zones);
|
||||
void Dismiss(HWND window);
|
||||
|
||||
std::map<HWND, ZoneIndexSet> SnappedWindows() const noexcept;
|
||||
@ -25,12 +17,9 @@ public :
|
||||
|
||||
void CycleWindows(HWND window, bool reverse);
|
||||
|
||||
const std::unique_ptr<ExtendWindowModeData>& ExtendWindowData();
|
||||
|
||||
private:
|
||||
std::map<HWND, ZoneIndexSet> m_windowIndexSet{};
|
||||
std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets{};
|
||||
std::unique_ptr<ExtendWindowModeData> m_extendData{}; // Needed for ExtendWindowByDirectionAndPosition
|
||||
|
||||
void InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
|
||||
HWND GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;
|
||||
|
@ -80,6 +80,13 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
inline void SetSettings(const Settings& settings)
|
||||
{
|
||||
m_settings = settings;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AddObserver(SettingsObserver& observer);
|
||||
void RemoveObserver(SettingsObserver& observer);
|
||||
|
||||
|
489
src/modules/fancyzones/FancyZonesLib/WindowKeyboardSnap.cpp
Normal file
489
src/modules/fancyzones/FancyZonesLib/WindowKeyboardSnap.cpp
Normal file
@ -0,0 +1,489 @@
|
||||
#include "pch.h"
|
||||
#include "WindowKeyboardSnap.h"
|
||||
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/trace.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
#include <FancyZonesLib/WorkArea.h>
|
||||
#include <FancyZonesLib/util.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
bool WindowKeyboardSnap::Snap(HWND window, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors)
|
||||
{
|
||||
return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && SnapHotkeyBasedOnZoneNumber(window, vkCode, monitor, activeWorkAreas, monitors);
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::Snap(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors)
|
||||
{
|
||||
if (!activeWorkAreas.contains(monitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// clean previous extension data
|
||||
m_extendData.Reset();
|
||||
|
||||
const auto& currentWorkArea = activeWorkAreas.at(monitor);
|
||||
if (monitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
|
||||
{
|
||||
// Multi monitor environment.
|
||||
// First, try to stay on the same monitor
|
||||
bool success = MoveByDirectionAndPosition(window, windowRect, vkCode, false, currentWorkArea.get());
|
||||
if (success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to snap on another monitor
|
||||
success = SnapBasedOnPositionOnAnotherMonitor(window, windowRect, vkCode, monitor, activeWorkAreas, monitors);
|
||||
if (success)
|
||||
{
|
||||
// Unsnap from previous work area
|
||||
currentWorkArea->Unsnap(window);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single monitor environment, or combined multi-monitor environment.
|
||||
return MoveByDirectionAndPosition(window, windowRect, vkCode, true, currentWorkArea.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::Extend(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
|
||||
{
|
||||
if (!activeWorkAreas.contains(monitor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// continue extension process
|
||||
const auto& workArea = activeWorkAreas.at(monitor);
|
||||
return Extend(window, windowRect, vkCode, workArea.get());
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::SnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode, HMONITOR current, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors)
|
||||
{
|
||||
// clean previous extension data
|
||||
m_extendData.Reset();
|
||||
|
||||
if (current && monitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
|
||||
{
|
||||
// Multi monitor environment.
|
||||
auto currMonitor = std::find(std::begin(monitors), std::end(monitors), current);
|
||||
do
|
||||
{
|
||||
if (activeWorkAreas.contains(*currMonitor))
|
||||
{
|
||||
const auto& workArea = activeWorkAreas.at(*currMonitor);
|
||||
|
||||
if (MoveByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea.get()))
|
||||
{
|
||||
// unassign from previous work area
|
||||
for (auto& [_, prevWorkArea] : activeWorkAreas)
|
||||
{
|
||||
if (prevWorkArea && workArea != prevWorkArea)
|
||||
{
|
||||
prevWorkArea->Unsnap(window);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
|
||||
if (vkCode == VK_RIGHT)
|
||||
{
|
||||
currMonitor = std::next(currMonitor);
|
||||
if (currMonitor == std::end(monitors))
|
||||
{
|
||||
currMonitor = std::begin(monitors);
|
||||
}
|
||||
}
|
||||
else if (vkCode == VK_LEFT)
|
||||
{
|
||||
if (currMonitor == std::begin(monitors))
|
||||
{
|
||||
currMonitor = std::end(monitors);
|
||||
}
|
||||
currMonitor = std::prev(currMonitor);
|
||||
}
|
||||
}
|
||||
} while (*currMonitor != current);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeWorkAreas.contains(current))
|
||||
{
|
||||
const auto& workArea = activeWorkAreas.at(current);
|
||||
bool moved = MoveByDirectionAndIndex(window, vkCode, FancyZonesSettings::settings().moveWindowAcrossMonitors /* cycle through zones */, workArea.get());
|
||||
|
||||
if (FancyZonesSettings::settings().restoreSize && !moved)
|
||||
{
|
||||
FancyZonesWindowUtils::RestoreWindowOrigin(window);
|
||||
FancyZonesWindowUtils::RestoreWindowSize(window);
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::SnapBasedOnPositionOnAnotherMonitor(HWND window, RECT windowRect, DWORD vkCode, HMONITOR current, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors)
|
||||
{
|
||||
// Extract zones from all other monitors and target one of them
|
||||
std::vector<RECT> zoneRects;
|
||||
std::vector<std::pair<ZoneIndex, WorkArea*>> zoneRectsInfo;
|
||||
RECT currentMonitorRect{ .top = 0, .bottom = -1 };
|
||||
|
||||
for (const auto& [monitor, monitorRect] : monitors)
|
||||
{
|
||||
if (monitor == current)
|
||||
{
|
||||
currentMonitorRect = monitorRect;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeWorkAreas.contains(monitor))
|
||||
{
|
||||
const auto& workArea = activeWorkAreas.at(monitor);
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += monitorRect.left;
|
||||
zoneRect.right += monitorRect.left;
|
||||
zoneRect.top += monitorRect.top;
|
||||
zoneRect.bottom += monitorRect.top;
|
||||
|
||||
zoneRects.emplace_back(zoneRect);
|
||||
zoneRectsInfo.emplace_back(zoneId, workArea.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
bool snapped = false;
|
||||
if (workArea)
|
||||
{
|
||||
snapped = workArea->Snap(window, { trueZoneIdx });
|
||||
}
|
||||
|
||||
if (snapped)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
|
||||
}
|
||||
|
||||
return snapped;
|
||||
}
|
||||
|
||||
// We reached the end of all monitors.
|
||||
// Try again, cycling on all monitors.
|
||||
// First, add zones from the origin monitor to zoneRects
|
||||
// Sanity check: the current monitor is valid
|
||||
if (currentMonitorRect.top <= currentMonitorRect.bottom)
|
||||
{
|
||||
const auto& currentWorkArea = activeWorkAreas.at(current);
|
||||
if (currentWorkArea)
|
||||
{
|
||||
const auto& layout = currentWorkArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += currentMonitorRect.left;
|
||||
zoneRect.right += currentMonitorRect.left;
|
||||
zoneRect.top += currentMonitorRect.top;
|
||||
zoneRect.bottom += currentMonitorRect.top;
|
||||
|
||||
zoneRects.emplace_back(zoneRect);
|
||||
zoneRectsInfo.emplace_back(zoneId, currentWorkArea.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT combinedRect = FancyZonesUtils::GetMonitorsCombinedRect<&MONITORINFOEX::rcWork>(monitors);
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
||||
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
|
||||
bool snapped = false;
|
||||
if (workArea)
|
||||
{
|
||||
snapped = workArea->Snap(window, { trueZoneIdx });
|
||||
}
|
||||
|
||||
if (snapped)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
|
||||
}
|
||||
|
||||
return snapped;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Giving up
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::MoveByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea)
|
||||
{
|
||||
if (!workArea)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
const auto& zones = layout->Zones();
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
if (!layout || zones.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zoneIndexes = layoutWindows.GetZoneIndexSetFromWindow(window);
|
||||
const auto numZones = zones.size();
|
||||
bool snapped = false;
|
||||
|
||||
// The window was not assigned to any zone here
|
||||
if (zoneIndexes.size() == 0)
|
||||
{
|
||||
const ZoneIndex zone = vkCode == VK_LEFT ? numZones - 1 : 0;
|
||||
snapped = workArea->Snap(window, { zone });
|
||||
}
|
||||
else
|
||||
{
|
||||
const ZoneIndex oldId = zoneIndexes[0];
|
||||
|
||||
// We reached the edge
|
||||
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast<int64_t>(numZones) - 1))
|
||||
{
|
||||
if (!cycle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const ZoneIndex zone = vkCode == VK_LEFT ? numZones - 1 : 0;
|
||||
snapped = workArea->Snap(window, { zone });
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't reach the edge
|
||||
if (vkCode == VK_LEFT)
|
||||
{
|
||||
snapped = workArea->Snap(window, { oldId - 1 });
|
||||
}
|
||||
else
|
||||
{
|
||||
snapped = workArea->Snap(window, { oldId + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (snapped)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
|
||||
}
|
||||
|
||||
return snapped;
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::MoveByDirectionAndPosition(HWND window, RECT windowRect, DWORD vkCode, bool cycle, WorkArea* const workArea)
|
||||
{
|
||||
if (!workArea)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
const auto& zones = layout->Zones();
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
if (!layout || zones.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<bool> usedZoneIndices(zones.size(), false);
|
||||
auto windowZones = layoutWindows.GetZoneIndexSetFromWindow(window);
|
||||
|
||||
for (const ZoneIndex id : windowZones)
|
||||
{
|
||||
usedZoneIndices[id] = true;
|
||||
}
|
||||
|
||||
std::vector<RECT> zoneRects;
|
||||
ZoneIndexSet freeZoneIndices;
|
||||
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
if (!usedZoneIndices[zoneId])
|
||||
{
|
||||
zoneRects.emplace_back(zones.at(zoneId).GetZoneRect());
|
||||
freeZoneIndices.emplace_back(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to coordinates relative to windowZone
|
||||
const auto& workAreaRect = workArea->GetWorkAreaRect();
|
||||
windowRect.top -= workAreaRect.top();
|
||||
windowRect.bottom -= workAreaRect.top();
|
||||
windowRect.left -= workAreaRect.left();
|
||||
windowRect.right -= workAreaRect.left();
|
||||
|
||||
ZoneIndex result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (static_cast<size_t>(result) < zoneRects.size())
|
||||
{
|
||||
bool success = workArea->Snap(window, { freeZoneIndices[result] });
|
||||
if (success)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(layout.get(), layoutWindows);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
else if (cycle)
|
||||
{
|
||||
// Try again from the position off the screen in the opposite direction to vkCode
|
||||
// Consider all zones as available
|
||||
zoneRects.resize(zones.size());
|
||||
std::transform(zones.begin(), zones.end(), zoneRects.begin(), [](auto zone) { return zone.second.GetZoneRect(); });
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, RECT(workAreaRect.left(), workAreaRect.top(), workAreaRect.right(), workAreaRect.bottom()), vkCode);
|
||||
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (static_cast<size_t>(result) < zoneRects.size())
|
||||
{
|
||||
bool success = workArea->Snap(window, { result });
|
||||
|
||||
if (success)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(layout.get(), layoutWindows);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WindowKeyboardSnap::Extend(HWND window, RECT windowRect, DWORD vkCode, WorkArea* const workArea)
|
||||
{
|
||||
if (!workArea)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
if (!layout || layout->Zones().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& zones = layout->Zones();
|
||||
auto appliedZones = layoutWindows.GetZoneIndexSetFromWindow(window);
|
||||
|
||||
std::vector<bool> usedZoneIndices(zones.size(), false);
|
||||
std::vector<RECT> zoneRects;
|
||||
ZoneIndexSet freeZoneIndices;
|
||||
|
||||
// If selectManyZones = true for the second time, use the last zone into which we moved
|
||||
// instead of the window rect and enable moving to all zones except the old one
|
||||
if (m_extendData.IsExtended(window))
|
||||
{
|
||||
usedZoneIndices[m_extendData.windowFinalIndex] = true;
|
||||
windowRect = zones.at(m_extendData.windowFinalIndex).GetZoneRect();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const ZoneIndex idx : appliedZones)
|
||||
{
|
||||
usedZoneIndices[idx] = true;
|
||||
}
|
||||
|
||||
// Move to coordinates relative to windowZone
|
||||
const auto& workAreaRect = workArea->GetWorkAreaRect();
|
||||
windowRect.top -= workAreaRect.top();
|
||||
windowRect.bottom -= workAreaRect.top();
|
||||
windowRect.left -= workAreaRect.left();
|
||||
windowRect.right -= workAreaRect.left();
|
||||
|
||||
m_extendData.Set(window);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < zones.size(); i++)
|
||||
{
|
||||
if (!usedZoneIndices[i])
|
||||
{
|
||||
zoneRects.emplace_back(zones.at(i).GetZoneRect());
|
||||
freeZoneIndices.emplace_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result >= zoneRects.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ZoneIndex targetZone = freeZoneIndices[result];
|
||||
ZoneIndexSet resultIndexSet;
|
||||
|
||||
// First time with selectManyZones = true for this window?
|
||||
if (m_extendData.windowFinalIndex == -1)
|
||||
{
|
||||
// Already zoned?
|
||||
if (appliedZones.size())
|
||||
{
|
||||
m_extendData.windowInitialIndexSet = appliedZones;
|
||||
m_extendData.windowFinalIndex = targetZone;
|
||||
resultIndexSet = layout->GetCombinedZoneRange(appliedZones, { targetZone });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_extendData.windowInitialIndexSet = { targetZone };
|
||||
m_extendData.windowFinalIndex = targetZone;
|
||||
resultIndexSet = { targetZone };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto deletethis = m_extendData.windowInitialIndexSet;
|
||||
m_extendData.windowFinalIndex = targetZone;
|
||||
resultIndexSet = layout->GetCombinedZoneRange(m_extendData.windowInitialIndexSet, { targetZone });
|
||||
}
|
||||
|
||||
bool success = workArea->Snap(window, resultIndexSet);
|
||||
if (success)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
56
src/modules/fancyzones/FancyZonesLib/WindowKeyboardSnap.h
Normal file
56
src/modules/fancyzones/FancyZonesLib/WindowKeyboardSnap.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/Zone.h>
|
||||
|
||||
class WorkArea;
|
||||
|
||||
class WindowKeyboardSnap
|
||||
{
|
||||
struct ExtendWindowModeData
|
||||
{
|
||||
HWND window{ nullptr };
|
||||
ZoneIndexSet windowInitialIndexSet{};
|
||||
ZoneIndex windowFinalIndex{ -1 };
|
||||
|
||||
bool IsExtended(HWND wnd) const
|
||||
{
|
||||
return window == wnd && windowFinalIndex != -1;
|
||||
}
|
||||
|
||||
void Set(HWND w)
|
||||
{
|
||||
window = w;
|
||||
windowFinalIndex = -1;
|
||||
windowInitialIndexSet.clear();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
window = nullptr;
|
||||
windowFinalIndex = -1;
|
||||
windowInitialIndexSet.clear();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
WindowKeyboardSnap() = default;
|
||||
~WindowKeyboardSnap() = default;
|
||||
|
||||
bool Snap(HWND window, HMONITOR activeMonitor, DWORD vkCode,
|
||||
const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas,
|
||||
const std::vector<HMONITOR>& monitors);
|
||||
bool Snap(HWND window, RECT windowRect, HMONITOR activeMonitor, DWORD vkCode,
|
||||
const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas,
|
||||
const std::vector<std::pair<HMONITOR, RECT>>& monitors);
|
||||
bool Extend(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
|
||||
|
||||
private:
|
||||
bool SnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors);
|
||||
bool SnapBasedOnPositionOnAnotherMonitor(HWND window, RECT windowRect, DWORD vkCode, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors);
|
||||
|
||||
bool MoveByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea);
|
||||
bool MoveByDirectionAndPosition(HWND window, RECT windowRect, DWORD vkCode, bool cycle, WorkArea* const workArea);
|
||||
bool Extend(HWND window, RECT windowRect, DWORD vkCode, WorkArea* const workArea);
|
||||
|
||||
ExtendWindowModeData m_extendData{}; // Needed for ExtendWindowByDirectionAndPosition
|
||||
};
|
244
src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
Normal file
244
src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
#include "pch.h"
|
||||
#include "WindowMouseSnap.h"
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
|
||||
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/NotificationUtil.h>
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
#include <FancyZonesLib/WorkArea.h>
|
||||
|
||||
#include <FancyZonesLib/trace.h>
|
||||
|
||||
#include <common/utils/elevation.h>
|
||||
|
||||
WindowMouseSnap::WindowMouseSnap(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas) :
|
||||
m_window(window),
|
||||
m_activeWorkAreas(activeWorkAreas),
|
||||
m_currentWorkArea(nullptr),
|
||||
m_snappingMode(false)
|
||||
{
|
||||
m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
||||
m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) &&
|
||||
(!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows);
|
||||
}
|
||||
|
||||
WindowMouseSnap::~WindowMouseSnap()
|
||||
{
|
||||
ResetWindowTransparency();
|
||||
}
|
||||
|
||||
std::unique_ptr<WindowMouseSnap> WindowMouseSnap::Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
|
||||
{
|
||||
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
|
||||
!FancyZonesWindowUtils::IsCandidateForZoning(window) ||
|
||||
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window))
|
||||
{
|
||||
// Notifies user if unable to drag elevated window
|
||||
FancyZonesNotifications::WarnIfElevationIsRequired();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<WindowMouseSnap>(new WindowMouseSnap(window, activeWorkAreas));
|
||||
}
|
||||
|
||||
bool WindowMouseSnap::MoveSizeStart(HMONITOR monitor, bool isSnapping)
|
||||
{
|
||||
auto iter = m_activeWorkAreas.find(monitor);
|
||||
if (iter == end(m_activeWorkAreas))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentWorkArea = iter->second.get();
|
||||
|
||||
SwitchSnappingMode(isSnapping);
|
||||
|
||||
if (m_currentWorkArea)
|
||||
{
|
||||
m_currentWorkArea->Unsnap(m_window);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowMouseSnap::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState)
|
||||
{
|
||||
auto iter = m_activeWorkAreas.find(monitor);
|
||||
if (isSnapping && iter != m_activeWorkAreas.end())
|
||||
{
|
||||
// The drag has moved to a different monitor.
|
||||
// Change work area
|
||||
if (iter->second.get() != m_currentWorkArea)
|
||||
{
|
||||
m_highlightedZones.Reset();
|
||||
|
||||
if (m_currentWorkArea)
|
||||
{
|
||||
if (!FancyZonesSettings::settings().showZonesOnAllMonitors)
|
||||
{
|
||||
m_currentWorkArea->HideZones();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentWorkArea->ShowZones({}, m_window);
|
||||
}
|
||||
}
|
||||
|
||||
m_currentWorkArea = iter->second.get();
|
||||
}
|
||||
|
||||
if (m_currentWorkArea)
|
||||
{
|
||||
POINT ptClient = ptScreen;
|
||||
MapWindowPoints(nullptr, m_currentWorkArea->GetWorkAreaWindow(), &ptClient, 1);
|
||||
const bool redraw = m_highlightedZones.Update(m_currentWorkArea->GetLayout().get(), ptClient, isSelectManyZonesState);
|
||||
if (redraw)
|
||||
{
|
||||
m_currentWorkArea->ShowZones(m_highlightedZones.Zones(), m_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitchSnappingMode(isSnapping);
|
||||
}
|
||||
|
||||
void WindowMouseSnap::MoveSizeEnd()
|
||||
{
|
||||
if (m_snappingMode)
|
||||
{
|
||||
const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
||||
const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window);
|
||||
|
||||
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
|
||||
m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) ||
|
||||
FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
||||
{
|
||||
// Abort the zoning, this is a Chromium based tab that is merged back with an existing window
|
||||
// or if the window is maximized by Windows when the cursor hits the screen top border
|
||||
}
|
||||
else if (m_currentWorkArea)
|
||||
{
|
||||
m_currentWorkArea->Snap(m_window, m_highlightedZones.Zones());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FancyZonesWindowUtils::ResetRoundCornersPreference(m_window);
|
||||
if (FancyZonesSettings::settings().restoreSize)
|
||||
{
|
||||
if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
||||
{
|
||||
::RemoveProp(m_window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||
}
|
||||
else if (!FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
||||
{
|
||||
FancyZonesWindowUtils::RestoreWindowSize(m_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitchSnappingMode(false);
|
||||
}
|
||||
|
||||
void WindowMouseSnap::SwitchSnappingMode(bool isSnapping)
|
||||
{
|
||||
if (!m_snappingMode && isSnapping) // turn on
|
||||
{
|
||||
m_highlightedZones.Reset();
|
||||
SetWindowTransparency();
|
||||
|
||||
if (FancyZonesSettings::settings().showZonesOnAllMonitors)
|
||||
{
|
||||
for (const auto& [_, workArea] : m_activeWorkAreas)
|
||||
{
|
||||
if (workArea)
|
||||
{
|
||||
workArea->ShowZones({}, m_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_currentWorkArea)
|
||||
{
|
||||
m_currentWorkArea->ShowZones({}, m_window);
|
||||
}
|
||||
|
||||
if (m_currentWorkArea)
|
||||
{
|
||||
m_currentWorkArea->Unsnap(m_window);
|
||||
Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows());
|
||||
}
|
||||
}
|
||||
else if (m_snappingMode && !isSnapping) // turn off
|
||||
{
|
||||
ResetWindowTransparency();
|
||||
m_highlightedZones.Reset();
|
||||
|
||||
// Hide all layouts (regardless of settings)
|
||||
for (auto& [_, workArea] : m_activeWorkAreas)
|
||||
{
|
||||
if (workArea)
|
||||
{
|
||||
workArea->HideZones();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_currentWorkArea)
|
||||
{
|
||||
Trace::WorkArea::MoveOrResizeEnd(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows());
|
||||
}
|
||||
}
|
||||
|
||||
m_snappingMode = isSnapping;
|
||||
}
|
||||
|
||||
void WindowMouseSnap::SetWindowTransparency()
|
||||
{
|
||||
if (FancyZonesSettings::settings().makeDraggedWindowTransparent)
|
||||
{
|
||||
m_windowProperties.exstyle = GetWindowLong(m_window, GWL_EXSTYLE);
|
||||
|
||||
SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle | WS_EX_LAYERED);
|
||||
|
||||
if (!GetLayeredWindowAttributes(m_window, &m_windowProperties.crKey, &m_windowProperties.alpha, &m_windowProperties.dwFlags))
|
||||
{
|
||||
Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA))
|
||||
{
|
||||
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
m_windowProperties.transparencySet = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowMouseSnap::ResetWindowTransparency()
|
||||
{
|
||||
if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowProperties.transparencySet)
|
||||
{
|
||||
bool reset = true;
|
||||
if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags))
|
||||
{
|
||||
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
||||
reset = false;
|
||||
}
|
||||
|
||||
if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0)
|
||||
{
|
||||
Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError()));
|
||||
reset = false;
|
||||
}
|
||||
|
||||
m_windowProperties.transparencySet = !reset;
|
||||
}
|
||||
}
|
@ -4,13 +4,13 @@
|
||||
|
||||
class WorkArea;
|
||||
|
||||
class WindowDrag
|
||||
class WindowMouseSnap
|
||||
{
|
||||
WindowDrag(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
|
||||
WindowMouseSnap(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<WindowDrag> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
|
||||
~WindowDrag();
|
||||
static std::unique_ptr<WindowMouseSnap> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
|
||||
~WindowMouseSnap();
|
||||
|
||||
bool MoveSizeStart(HMONITOR monitor, bool isSnapping);
|
||||
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState);
|
@ -456,15 +456,19 @@ RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
// Take care of borders
|
||||
RECT frameRect{};
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
|
||||
// Skip when windowOfRect is not initialized (in unit tests)
|
||||
if (windowOfRect)
|
||||
{
|
||||
LONG leftMargin = frameRect.left - windowRect.left;
|
||||
LONG rightMargin = frameRect.right - windowRect.right;
|
||||
LONG bottomMargin = frameRect.bottom - windowRect.bottom;
|
||||
newWindowRect.left -= leftMargin;
|
||||
newWindowRect.right -= rightMargin;
|
||||
newWindowRect.bottom -= bottomMargin;
|
||||
RECT frameRect{};
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
|
||||
{
|
||||
LONG leftMargin = frameRect.left - windowRect.left;
|
||||
LONG rightMargin = frameRect.right - windowRect.right;
|
||||
LONG bottomMargin = frameRect.bottom - windowRect.bottom;
|
||||
newWindowRect.left -= leftMargin;
|
||||
newWindowRect.right -= rightMargin;
|
||||
newWindowRect.bottom -= bottomMargin;
|
||||
}
|
||||
}
|
||||
|
||||
// Take care of windows that cannot be resized
|
||||
@ -475,7 +479,10 @@ RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect
|
||||
}
|
||||
|
||||
// Convert to screen coordinates
|
||||
MapWindowRect(windowOfRect, nullptr, &newWindowRect);
|
||||
if (windowOfRect)
|
||||
{
|
||||
MapWindowRect(windowOfRect, nullptr, &newWindowRect);
|
||||
}
|
||||
|
||||
return newWindowRect;
|
||||
}
|
||||
|
@ -1,26 +1,16 @@
|
||||
#include "pch.h"
|
||||
#include "WorkArea.h"
|
||||
|
||||
#include <common/logger/call_tracer.h>
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
#include "FancyZonesData/AppliedLayouts.h"
|
||||
#include "FancyZonesData/AppZoneHistory.h"
|
||||
#include "FancyZonesDataTypes.h"
|
||||
#include "SettingsObserver.h"
|
||||
#include "ZonesOverlay.h"
|
||||
#include "trace.h"
|
||||
#include "on_thread_executor.h"
|
||||
#include "Settings.h"
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/VirtualDesktop.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
|
||||
#include <ShellScalingApi.h>
|
||||
#include <mutex>
|
||||
#include <fileapi.h>
|
||||
|
||||
// disabling warning 4458 - declaration of 'identifier' hides class member
|
||||
// to avoid warnings from GDI files - can't add winRT directory to external code
|
||||
// in the Cpp.Build.props
|
||||
@ -127,284 +117,41 @@ WorkArea::~WorkArea()
|
||||
windowPool.FreeZonesOverlayWindow(m_window);
|
||||
}
|
||||
|
||||
void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index)
|
||||
bool WorkArea::Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition)
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, { index });
|
||||
}
|
||||
|
||||
void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/)
|
||||
{
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty())
|
||||
if (!m_layout || zones.empty())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
|
||||
m_layoutWindows.Assign(window, zones);
|
||||
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, m_layout->Id(), zones);
|
||||
|
||||
if (updatePosition)
|
||||
{
|
||||
const auto rect = m_layout->GetCombinedZonesRect(indexSet);
|
||||
if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0)
|
||||
{
|
||||
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
||||
}
|
||||
const auto rect = m_layout->GetCombinedZonesRect(zones);
|
||||
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
||||
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
||||
}
|
||||
|
||||
SnapWindow(window, indexSet);
|
||||
return FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
|
||||
}
|
||||
|
||||
bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle)
|
||||
bool WorkArea::Unsnap(HWND window)
|
||||
{
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
if (!m_layout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
const auto numZones = m_layout->Zones().size();
|
||||
|
||||
// The window was not assigned to any zone here
|
||||
if (zoneIndexes.size() == 0)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ZoneIndex oldId = zoneIndexes[0];
|
||||
|
||||
// We reached the edge
|
||||
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast<int64_t>(numZones) - 1))
|
||||
{
|
||||
if (!cycle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't reach the edge
|
||||
if (vkCode == VK_LEFT)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, oldId - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, oldId + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_layoutWindows.Dismiss(window);
|
||||
AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, m_layout->Id());
|
||||
FancyZonesWindowProperties::RemoveZoneIndexProperty(window);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle)
|
||||
{
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& zones = m_layout->Zones();
|
||||
std::vector<bool> usedZoneIndices(zones.size(), false);
|
||||
auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
|
||||
for (const ZoneIndex id : windowZones)
|
||||
{
|
||||
usedZoneIndices[id] = true;
|
||||
}
|
||||
|
||||
std::vector<RECT> zoneRects;
|
||||
ZoneIndexSet freeZoneIndices;
|
||||
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
if (!usedZoneIndices[zoneId])
|
||||
{
|
||||
zoneRects.emplace_back(zones.at(zoneId).GetZoneRect());
|
||||
freeZoneIndices.emplace_back(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
RECT windowRect;
|
||||
if (!GetWindowRect(window, &windowRect))
|
||||
{
|
||||
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move to coordinates relative to windowZone
|
||||
windowRect.top -= m_workAreaRect.top();
|
||||
windowRect.bottom -= m_workAreaRect.top();
|
||||
windowRect.left -= m_workAreaRect.left();
|
||||
windowRect.right -= m_workAreaRect.left();
|
||||
|
||||
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, freeZoneIndices[result]);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
||||
return true;
|
||||
}
|
||||
else if (cycle)
|
||||
{
|
||||
// Try again from the position off the screen in the opposite direction to vkCode
|
||||
// Consider all zones as available
|
||||
zoneRects.resize(zones.size());
|
||||
std::transform(zones.begin(), zones.end(), zoneRects.begin(), [](auto zone) { return zone.second.GetZoneRect(); });
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, RECT(m_workAreaRect.left(), m_workAreaRect.top(), m_workAreaRect.right(), m_workAreaRect.bottom()), vkCode);
|
||||
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, result);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode)
|
||||
{
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT windowRect;
|
||||
if (!GetWindowRect(window, &windowRect))
|
||||
{
|
||||
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& zones = m_layout->Zones();
|
||||
auto appliedZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
const auto& extendModeData = m_layoutWindows->ExtendWindowData();
|
||||
|
||||
std::vector<bool> usedZoneIndices(zones.size(), false);
|
||||
std::vector<RECT> zoneRects;
|
||||
ZoneIndexSet freeZoneIndices;
|
||||
|
||||
// If selectManyZones = true for the second time, use the last zone into which we moved
|
||||
// instead of the window rect and enable moving to all zones except the old one
|
||||
auto finalIndexIt = extendModeData->windowFinalIndex.find(window);
|
||||
if (finalIndexIt != extendModeData->windowFinalIndex.end())
|
||||
{
|
||||
usedZoneIndices[finalIndexIt->second] = true;
|
||||
windowRect = zones.at(finalIndexIt->second).GetZoneRect();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const ZoneIndex idx : appliedZones)
|
||||
{
|
||||
usedZoneIndices[idx] = true;
|
||||
}
|
||||
// Move to coordinates relative to windowZone
|
||||
windowRect.top -= m_workAreaRect.top();
|
||||
windowRect.bottom -= m_workAreaRect.top();
|
||||
windowRect.left -= m_workAreaRect.left();
|
||||
windowRect.right -= m_workAreaRect.left();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < zones.size(); i++)
|
||||
{
|
||||
if (!usedZoneIndices[i])
|
||||
{
|
||||
zoneRects.emplace_back(zones.at(i).GetZoneRect());
|
||||
freeZoneIndices.emplace_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
ZoneIndex targetZone = freeZoneIndices[result];
|
||||
ZoneIndexSet resultIndexSet;
|
||||
|
||||
// First time with selectManyZones = true for this window?
|
||||
if (finalIndexIt == extendModeData->windowFinalIndex.end())
|
||||
{
|
||||
// Already zoned?
|
||||
if (appliedZones.size())
|
||||
{
|
||||
extendModeData->windowInitialIndexSet[window] = appliedZones;
|
||||
extendModeData->windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = m_layout->GetCombinedZoneRange(appliedZones, { targetZone });
|
||||
}
|
||||
else
|
||||
{
|
||||
extendModeData->windowInitialIndexSet[window] = { targetZone };
|
||||
extendModeData->windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = { targetZone };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto deletethis = extendModeData->windowInitialIndexSet[window];
|
||||
extendModeData->windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone });
|
||||
}
|
||||
|
||||
const auto rect = m_layout->GetCombinedZonesRect(resultIndexSet);
|
||||
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
||||
|
||||
SnapWindow(window, resultIndexSet, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend)
|
||||
{
|
||||
if (!m_layoutWindows || !m_layout)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (extend)
|
||||
{
|
||||
m_layoutWindows->Extend(window, zones);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_layoutWindows->Assign(window, zones);
|
||||
}
|
||||
|
||||
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zones);
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
|
||||
}
|
||||
|
||||
void WorkArea::UnsnapWindow(HWND window)
|
||||
{
|
||||
if (!m_layoutWindows || !m_layout)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_layoutWindows->Dismiss(window);
|
||||
|
||||
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, guidStr.value());
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::RemoveZoneIndexProperty(window);
|
||||
}
|
||||
|
||||
const GUID WorkArea::GetLayoutId() const noexcept
|
||||
{
|
||||
if (m_layout)
|
||||
@ -415,29 +162,7 @@ const GUID WorkArea::GetLayoutId() const noexcept
|
||||
return GUID{};
|
||||
}
|
||||
|
||||
ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const
|
||||
{
|
||||
if (m_layout)
|
||||
{
|
||||
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
return AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, guidStr.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"Failed to convert to string layout GUID on the requested work area");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"No layout initialized on the requested work area");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
|
||||
void WorkArea::ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
|
||||
{
|
||||
if (m_layout && m_zonesOverlay)
|
||||
{
|
||||
@ -447,7 +172,7 @@ void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindo
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::HideZonesOverlay()
|
||||
void WorkArea::HideZones()
|
||||
{
|
||||
if (m_zonesOverlay)
|
||||
{
|
||||
@ -465,15 +190,10 @@ void WorkArea::FlashZones()
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::UpdateActiveZoneSet()
|
||||
void WorkArea::InitLayout()
|
||||
{
|
||||
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
|
||||
if (!isLayoutAlreadyApplied)
|
||||
{
|
||||
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
|
||||
}
|
||||
InitLayout({});
|
||||
|
||||
CalculateZoneSet();
|
||||
if (m_window && m_layout)
|
||||
{
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
@ -482,24 +202,16 @@ void WorkArea::UpdateActiveZoneSet()
|
||||
|
||||
void WorkArea::UpdateWindowPositions()
|
||||
{
|
||||
if (!m_layoutWindows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& snappedWindows = m_layoutWindows->SnappedWindows();
|
||||
const auto& snappedWindows = m_layoutWindows.SnappedWindows();
|
||||
for (const auto& [window, zones] : snappedWindows)
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, zones, true);
|
||||
Snap(window, zones, true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::CycleWindows(HWND window, bool reverse)
|
||||
{
|
||||
if (m_layoutWindows)
|
||||
{
|
||||
m_layoutWindows->CycleWindows(window, reverse);
|
||||
}
|
||||
m_layoutWindows.CycleWindows(window, reverse);
|
||||
}
|
||||
|
||||
#pragma region private
|
||||
@ -537,6 +249,46 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
|
||||
CalculateZoneSet();
|
||||
}
|
||||
|
||||
void WorkArea::InitSnappedWindows()
|
||||
{
|
||||
static bool updatePositionOnceOnStartFlag = true;
|
||||
Logger::info(L"Init work area {} windows, update positions = {}", m_uniqueId.toString(), updatePositionOnceOnStartFlag);
|
||||
|
||||
for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop())
|
||||
{
|
||||
auto indexes = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
|
||||
if (indexes.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_uniqueId.monitorId.monitor) // one work area across monitors
|
||||
{
|
||||
Snap(window, indexes, updatePositionOnceOnStartFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
if (monitor && m_uniqueId.monitorId.monitor == monitor)
|
||||
{
|
||||
// prioritize snapping on the current monitor if the window was snapped to several work areas
|
||||
Snap(window, indexes, updatePositionOnceOnStartFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the window is not snapped on the current monitor, then check the others
|
||||
auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, GetLayoutId());
|
||||
if (savedIndexes == indexes)
|
||||
{
|
||||
Snap(window, indexes, updatePositionOnceOnStartFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePositionOnceOnStartFlag = false;
|
||||
}
|
||||
|
||||
void WorkArea::CalculateZoneSet()
|
||||
{
|
||||
const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId);
|
||||
@ -548,11 +300,6 @@ void WorkArea::CalculateZoneSet()
|
||||
|
||||
m_layout = std::make_unique<Layout>(appliedLayout.value());
|
||||
m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor);
|
||||
|
||||
if (!m_layoutWindows)
|
||||
{
|
||||
m_layoutWindows = std::make_unique<LayoutAssignedWindows>();
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/FancyZonesDataTypes.h>
|
||||
#include <FancyZonesLib/Layout.h>
|
||||
#include <FancyZonesLib/LayoutAssignedWindows.h>
|
||||
|
||||
@ -39,26 +38,20 @@ public:
|
||||
|
||||
FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; }
|
||||
const std::unique_ptr<Layout>& GetLayout() const noexcept { return m_layout; }
|
||||
const std::unique_ptr<LayoutAssignedWindows>& GetLayoutWindows() const noexcept { return m_layoutWindows; }
|
||||
const LayoutAssignedWindows& GetLayoutWindows() const noexcept { return m_layoutWindows; }
|
||||
const HWND GetWorkAreaWindow() const noexcept { return m_window; }
|
||||
const GUID GetLayoutId() const noexcept;
|
||||
const FancyZonesUtils::Rect& GetWorkAreaRect() const noexcept { return m_workAreaRect; }
|
||||
|
||||
ZoneIndexSet GetWindowZoneIndexes(HWND window) const;
|
||||
|
||||
void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index);
|
||||
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true);
|
||||
bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle);
|
||||
bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle);
|
||||
bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode);
|
||||
|
||||
void SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend = false);
|
||||
void UnsnapWindow(HWND window);
|
||||
|
||||
void UpdateActiveZoneSet();
|
||||
void InitLayout();
|
||||
void InitSnappedWindows();
|
||||
void UpdateWindowPositions();
|
||||
|
||||
void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr);
|
||||
void HideZonesOverlay();
|
||||
bool Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition = true);
|
||||
bool Unsnap(HWND window);
|
||||
|
||||
void ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr);
|
||||
void HideZones();
|
||||
void FlashZones();
|
||||
|
||||
void CycleWindows(HWND window, bool reverse);
|
||||
@ -79,6 +72,6 @@ private:
|
||||
const FancyZonesDataTypes::WorkAreaId m_uniqueId;
|
||||
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
|
||||
std::unique_ptr<Layout> m_layout;
|
||||
std::unique_ptr<LayoutAssignedWindows> m_layoutWindows;
|
||||
LayoutAssignedWindows m_layoutWindows{};
|
||||
std::unique_ptr<ZonesOverlay> m_zonesOverlay;
|
||||
};
|
||||
|
@ -90,17 +90,17 @@ struct ZoneSetInfo
|
||||
};
|
||||
|
||||
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, const LayoutAssignedWindows& layoutWindows) noexcept
|
||||
{
|
||||
ZoneSetInfo info;
|
||||
if (layout && layoutWindows)
|
||||
if (layout)
|
||||
{
|
||||
auto zones = layout->Zones();
|
||||
info.NumberOfZones = zones.size();
|
||||
info.NumberOfWindows = 0;
|
||||
for (int i = 0; i < static_cast<int>(zones.size()); i++)
|
||||
{
|
||||
if (!layoutWindows->IsZoneEmpty(i))
|
||||
if (!layoutWindows.IsZoneEmpty(i))
|
||||
{
|
||||
info.NumberOfWindows++;
|
||||
}
|
||||
@ -258,7 +258,7 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept
|
||||
TraceLoggingBoolean(shortcutUsed, QuickLayoutSwitchedWithShortcutUsed));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept
|
||||
void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
@ -271,7 +271,7 @@ void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssign
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept
|
||||
void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
@ -356,7 +356,7 @@ void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept
|
||||
TraceLoggingValue(wParam, KeyboardValueKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
@ -369,7 +369,7 @@ void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
@ -382,7 +382,7 @@ void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ La
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept
|
||||
void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows, InputMode mode) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
|
@ -19,8 +19,8 @@ public:
|
||||
static void EditorLaunched(int value) noexcept;
|
||||
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||
static void QuickLayoutSwitched(bool shortcutUsed) noexcept;
|
||||
static void SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void KeyboardSnapWindowToZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void SnapNewWindowIntoZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
|
||||
static void KeyboardSnapWindowToZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
|
||||
};
|
||||
|
||||
static void SettingsTelemetry(const Settings& settings) noexcept;
|
||||
@ -36,8 +36,8 @@ public:
|
||||
};
|
||||
|
||||
static void KeyUp(WPARAM wparam) noexcept;
|
||||
static void MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept;
|
||||
static void MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
|
||||
static void MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
|
||||
static void CycleActiveZoneSet(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows, InputMode mode) noexcept;
|
||||
};
|
||||
};
|
||||
|
@ -123,6 +123,15 @@ namespace FancyZonesUtils
|
||||
monitorInfo = std::move(sortedMonitorInfo);
|
||||
}
|
||||
|
||||
std::vector<HMONITOR> GetMonitorsOrdered()
|
||||
{
|
||||
auto monitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
FancyZonesUtils::OrderMonitors(monitors);
|
||||
std::vector<HMONITOR> output;
|
||||
std::transform(std::begin(monitors), std::end(monitors), std::back_inserter(output), [](const auto& info) { return info.first; });
|
||||
return output;
|
||||
}
|
||||
|
||||
bool IsValidGuid(const std::wstring& str)
|
||||
{
|
||||
GUID id;
|
||||
|
@ -143,13 +143,12 @@ namespace FancyZonesUtils
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
RECT GetAllMonitorsCombinedRect()
|
||||
RECT GetMonitorsCombinedRect(const std::vector<std::pair<HMONITOR, RECT>>& monitorRects)
|
||||
{
|
||||
auto allMonitors = GetAllMonitorRects<member>();
|
||||
bool empty = true;
|
||||
RECT result{ 0, 0, 0, 0 };
|
||||
|
||||
for (auto& [monitor, rect] : allMonitors)
|
||||
for (auto& [monitor, rect] : monitorRects)
|
||||
{
|
||||
if (empty)
|
||||
{
|
||||
@ -168,6 +167,13 @@ namespace FancyZonesUtils
|
||||
return result;
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
RECT GetAllMonitorsCombinedRect()
|
||||
{
|
||||
auto allMonitors = GetAllMonitorRects<member>();
|
||||
return GetMonitorsCombinedRect<member>(allMonitors);
|
||||
}
|
||||
|
||||
constexpr RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept
|
||||
{
|
||||
LONG deltaX = 0, deltaY = 0;
|
||||
@ -196,6 +202,7 @@ namespace FancyZonesUtils
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
std::vector<HMONITOR> GetMonitorsOrdered();
|
||||
|
||||
bool IsValidGuid(const std::wstring& str);
|
||||
std::optional<GUID> GuidFromString(const std::wstring& str) noexcept;
|
||||
|
@ -207,22 +207,22 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (AppLastZoneInvalidWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
};
|
||||
const auto window = Mocks::Window();
|
||||
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
|
||||
|
||||
const int expectedZoneIndex = 1;
|
||||
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { expectedZoneIndex }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { expectedZoneIndex }));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneNullWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
@ -230,12 +230,12 @@ namespace FancyZonesUnitTests
|
||||
const auto window = nullptr;
|
||||
|
||||
const int expectedZoneIndex = 1;
|
||||
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { expectedZoneIndex }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { expectedZoneIndex }));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastdeviceIdTest)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId1{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
@ -247,15 +247,15 @@ namespace FancyZonesUnitTests
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
const int expectedZoneIndex = 10;
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, zoneSetId, { expectedZoneIndex }));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, zoneSetId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, zoneSetId));
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, layoutId, { expectedZoneIndex }));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, layoutId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, layoutId));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneSetIdTest)
|
||||
{
|
||||
const std::wstring zoneSetId1 = L"{B7A1F5A9-9DC2-4505-84AB-993253839093}";
|
||||
const std::wstring zoneSetId2 = L"{B7A1F5A9-9DC2-4505-84AB-993253839094}";
|
||||
const auto layoutId1 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839093}").value();
|
||||
const auto layoutId2 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839094}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
@ -263,56 +263,56 @@ namespace FancyZonesUnitTests
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
const int expectedZoneIndex = 10;
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId1, { expectedZoneIndex }));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId1));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId2));
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId1, { expectedZoneIndex }));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId1));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId2));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneRemoveWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{B7A1F5A9-9DC2-4505-84AB-993253839093}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839093}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
};
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { 1 }));
|
||||
Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId));
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { 1 }));
|
||||
Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneRemoveUnknownWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
};
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneRemoveUnknownZoneSetId)
|
||||
{
|
||||
const std::wstring zoneSetIdToInsert = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const std::wstring zoneSetIdToRemove = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}";
|
||||
const auto layoutIdToInsert = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const auto layoutIdToRemove = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
};
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetIdToInsert, { 1 }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetIdToRemove));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetIdToInsert));
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutIdToInsert, { 1 }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutIdToRemove));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutIdToInsert));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneRemoveUnknownWindowId)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaIdToInsert{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
@ -323,20 +323,20 @@ namespace FancyZonesUnitTests
|
||||
};
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, zoneSetId, { 1 }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, zoneSetId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, zoneSetId));
|
||||
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, layoutId, { 1 }));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, layoutId));
|
||||
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, layoutId));
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneRemoveNullWindow)
|
||||
{
|
||||
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
|
||||
const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
|
||||
const FancyZonesDataTypes::WorkAreaId workAreaId{
|
||||
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
|
||||
};
|
||||
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, zoneSetId));
|
||||
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, layoutId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -140,10 +140,9 @@ namespace FancyZonesUnitTests
|
||||
data.zoneCount = 4;
|
||||
|
||||
// prepare settings
|
||||
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey);
|
||||
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest)));
|
||||
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json());
|
||||
FancyZonesSettings::instance().LoadSettings();
|
||||
auto settings = FancyZonesSettings::settings();
|
||||
settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
|
||||
FancyZonesSettings::instance().SetSettings(settings);
|
||||
|
||||
auto layout = std::make_unique<Layout>(data);
|
||||
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());
|
||||
@ -168,10 +167,9 @@ namespace FancyZonesUnitTests
|
||||
data.zoneCount = 4;
|
||||
|
||||
// prepare settings
|
||||
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey);
|
||||
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest)));
|
||||
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json());
|
||||
FancyZonesSettings::instance().LoadSettings();
|
||||
auto settings = FancyZonesSettings::settings();
|
||||
settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
|
||||
FancyZonesSettings::instance().SetSettings(settings);
|
||||
|
||||
auto layout = std::make_unique<Layout>(data);
|
||||
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());
|
||||
|
@ -55,6 +55,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="Util.Spec.cpp" />
|
||||
<ClCompile Include="Util.cpp" />
|
||||
<ClCompile Include="WindowKeyboardSnap.Spec.cpp" />
|
||||
<ClCompile Include="WorkArea.Spec.cpp" />
|
||||
<ClCompile Include="WorkAreaIdTests.Spec.cpp" />
|
||||
<ClCompile Include="Zone.Spec.cpp" />
|
||||
|
@ -63,6 +63,9 @@
|
||||
<ClCompile Include="DefaultLayoutsTests.Spec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WindowKeyboardSnap.Spec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,6 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
|
||||
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
|
||||
|
||||
std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName());
|
||||
}
|
||||
|
||||
@ -58,7 +57,6 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
Assert::IsNotNull(layout.get());
|
||||
Assert::IsNotNull(workArea->GetLayoutWindows().get());
|
||||
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
|
||||
Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size()));
|
||||
}
|
||||
@ -73,7 +71,6 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
Assert::IsNotNull(layout.get());
|
||||
Assert::IsNotNull(workArea->GetLayoutWindows().get());
|
||||
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
|
||||
Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size()));
|
||||
}
|
||||
@ -102,7 +99,6 @@ namespace FancyZonesUnitTests
|
||||
|
||||
auto actualWorkArea = WorkArea::Create(m_hInst, m_workAreaId, parentUniqueId, m_workAreaRect);
|
||||
Assert::IsNotNull(actualWorkArea->GetLayout().get());
|
||||
Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get());
|
||||
|
||||
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_workAreaId));
|
||||
const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_workAreaId);
|
||||
@ -179,428 +175,46 @@ namespace FancyZonesUnitTests
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS (WorkAreaMoveWindowUnitTests)
|
||||
TEST_CLASS (WorkAreaSnapUnitTests)
|
||||
{
|
||||
const std::wstring m_virtualDesktopIdStr = L"{A998CA86-F08D-4BCA-AED8-77F5C8FC9925}";
|
||||
const FancyZonesDataTypes::WorkAreaId m_workAreaId{
|
||||
HINSTANCE m_hInst{};
|
||||
const HMONITOR m_monitor = Mocks::Monitor();
|
||||
const FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) };
|
||||
const FancyZonesDataTypes::WorkAreaId m_parentUniqueId = {};
|
||||
const FancyZonesDataTypes::WorkAreaId m_workAreaId = {
|
||||
.monitorId = {
|
||||
.monitor = Mocks::Monitor(),
|
||||
.deviceId = {
|
||||
.id = L"DELA026",
|
||||
.monitor = m_monitor,
|
||||
.deviceId = {
|
||||
.id = L"device-id-1",
|
||||
.instanceId = L"5&10a58c63&0&UID16777488",
|
||||
.number = 1,
|
||||
},
|
||||
.serialNumber = L"serial-number"
|
||||
},
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(m_virtualDesktopIdStr).value()
|
||||
.serialNumber = L"serial-number-1" },
|
||||
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{310F2924-B587-4D87-97C2-90031BDBE3F1}").value()
|
||||
};
|
||||
|
||||
FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty
|
||||
|
||||
HINSTANCE m_hInst{};
|
||||
FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) };
|
||||
|
||||
void PrepareEmptyLayout()
|
||||
{
|
||||
json::JsonObject root{};
|
||||
json::JsonArray layoutsArray{};
|
||||
|
||||
{
|
||||
json::JsonObject layout{};
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Blank)));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(false));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(0));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(0));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(0));
|
||||
|
||||
json::JsonObject workAreaId{};
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr));
|
||||
|
||||
json::JsonObject obj{};
|
||||
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, workAreaId);
|
||||
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
|
||||
|
||||
layoutsArray.Append(obj);
|
||||
}
|
||||
|
||||
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
|
||||
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
|
||||
|
||||
AppliedLayouts::instance().LoadData();
|
||||
}
|
||||
|
||||
void PrepareGridLayout()
|
||||
{
|
||||
json::JsonObject root{};
|
||||
json::JsonArray layoutsArray{};
|
||||
|
||||
{
|
||||
json::JsonObject layout{};
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Grid)));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(false));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(0));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(4));
|
||||
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(20));
|
||||
|
||||
json::JsonObject workAreaId{};
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number));
|
||||
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr));
|
||||
|
||||
json::JsonObject obj{};
|
||||
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, workAreaId);
|
||||
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
|
||||
|
||||
layoutsArray.Append(obj);
|
||||
}
|
||||
|
||||
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
|
||||
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
|
||||
|
||||
AppliedLayouts::instance().LoadData();
|
||||
}
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init) noexcept
|
||||
{
|
||||
AppZoneHistory::instance().LoadData();
|
||||
AppliedLayouts::instance().LoadData();
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(CleanUp) noexcept
|
||||
{
|
||||
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
|
||||
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
|
||||
}
|
||||
|
||||
TEST_METHOD (EmptyZonesMoveLeftByIndex)
|
||||
{
|
||||
// prepare
|
||||
PrepareEmptyLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (EmptyZonesRightByIndex)
|
||||
{
|
||||
// prepare
|
||||
PrepareEmptyLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveLeftNonAppliedWindowByIndex)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 3 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveRightNonAppliedWindowByIndex)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByIndex)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
|
||||
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByIndexCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ static_cast<ZoneIndex>(workArea->GetLayout()->Zones().size() - 1) } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByIndexNoCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, false);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (EmptyZonesMoveByPosition)
|
||||
{
|
||||
// prepare
|
||||
PrepareEmptyLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveLeftNonAppliedWindowByPosition)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveRightNonAppliedWindowByPosition)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_RIGHT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowHorizontallyByPosition)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_RIGHT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowVerticallyByPosition)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_DOWN, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByPositionHorizontallyCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByPositionHorizontallyNoCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, false);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByPositionVerticallyCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_UP, true);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
Assert::IsTrue(ZoneIndexSet{ 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveAppliedWindowByPositionVerticallyNoCycle)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_UP, false);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (ExtendZoneHorizontally)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->ExtendWindowByDirectionAndPosition(window, VK_RIGHT);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0, 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (ExtendZoneVertically)
|
||||
{
|
||||
// prepare
|
||||
PrepareGridLayout();
|
||||
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
|
||||
|
||||
// test
|
||||
workArea->ExtendWindowByDirectionAndPosition(window, VK_DOWN);
|
||||
|
||||
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
|
||||
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(ZoneIndexSet{ 0, 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
|
||||
{
|
||||
LayoutData layout{
|
||||
.uuid = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC58}").value(),
|
||||
.type = FancyZonesDataTypes::ZoneSetLayoutType::Grid,
|
||||
.showSpacing = false,
|
||||
.spacing = 0,
|
||||
.zoneCount = 4,
|
||||
.sensitivityRadius = 20,
|
||||
};
|
||||
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
|
||||
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
@ -610,7 +224,10 @@ namespace FancyZonesUnitTests
|
||||
SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW);
|
||||
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
|
||||
|
||||
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
|
||||
Assert::IsTrue(workArea->Snap(window, { 1 }, true));
|
||||
|
||||
// wait for the window to be resized
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
RECT inZoneRect;
|
||||
GetWindowRect(window, &inZoneRect);
|
||||
@ -619,13 +236,46 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
|
||||
}
|
||||
|
||||
TEST_METHOD (WhenWindowIsResizablePlacingItIntoTheZoneShouldResizeIt)
|
||||
{
|
||||
LayoutData layout{
|
||||
.uuid = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC58}").value(),
|
||||
.type = FancyZonesDataTypes::ZoneSetLayoutType::Grid,
|
||||
.showSpacing = false,
|
||||
.spacing = 0,
|
||||
.zoneCount = 4,
|
||||
.sensitivityRadius = 20,
|
||||
};
|
||||
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
|
||||
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
SetWindowPos(window, nullptr, 150, 150, 450, 550, SWP_SHOWWINDOW);
|
||||
|
||||
Assert::IsTrue(workArea->Snap(window, { 1 }, true));
|
||||
|
||||
// wait for the window to be resized
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
RECT zonedWindowRect;
|
||||
GetWindowRect(window, &zonedWindowRect);
|
||||
|
||||
RECT zoneRect = workArea->GetLayout()->Zones().at(1).GetZoneRect();
|
||||
|
||||
Assert::AreEqual(zoneRect.left, zonedWindowRect.left);
|
||||
Assert::AreEqual(zoneRect.right, zonedWindowRect.right);
|
||||
Assert::AreEqual(zoneRect.top, zonedWindowRect.top);
|
||||
Assert::AreEqual(zoneRect.bottom, zonedWindowRect.bottom);
|
||||
}
|
||||
|
||||
TEST_METHOD (SnapWindowPropertyTest)
|
||||
{
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
const ZoneIndexSet expected = { 1, 2 };
|
||||
workArea->SnapWindow(window, expected);
|
||||
Assert::IsTrue(workArea->Snap(window, expected));
|
||||
|
||||
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
|
||||
Assert::AreEqual(expected.size(), actual.size());
|
||||
@ -641,7 +291,7 @@ namespace FancyZonesUnitTests
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
const ZoneIndexSet expected = { 1, 2 };
|
||||
workArea->SnapWindow(window, expected);
|
||||
Assert::IsTrue(workArea->Snap(window, expected));
|
||||
|
||||
const auto processPath = get_process_path(window);
|
||||
const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId);
|
||||
@ -654,13 +304,25 @@ namespace FancyZonesUnitTests
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD (SnapLayoutAssignedWindowsTest)
|
||||
{
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
const ZoneIndexSet expected = { 1, 2 };
|
||||
Assert::IsTrue(workArea->Snap(window, expected));
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(expected == layoutWindows.GetZoneIndexSetFromWindow(window));
|
||||
}
|
||||
|
||||
TEST_METHOD (UnsnapPropertyTest)
|
||||
{
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
workArea->SnapWindow(window, { 1, 2 });
|
||||
workArea->UnsnapWindow(window);
|
||||
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
|
||||
Assert::IsTrue(workArea->Unsnap(window));
|
||||
|
||||
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
|
||||
Assert::IsTrue(actual.empty());
|
||||
@ -671,13 +333,25 @@ namespace FancyZonesUnitTests
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
workArea->SnapWindow(window, { 1, 2 });
|
||||
workArea->UnsnapWindow(window);
|
||||
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
|
||||
Assert::IsTrue(workArea->Unsnap(window));
|
||||
|
||||
const auto processPath = get_process_path(window);
|
||||
const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId);
|
||||
|
||||
Assert::IsFalse(history.has_value());
|
||||
}
|
||||
|
||||
TEST_METHOD (UnsnapLayoutAssignedWindowsTest)
|
||||
{
|
||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||
const auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
|
||||
Assert::IsTrue(workArea->Unsnap(window));
|
||||
|
||||
const auto& layoutWindows = workArea->GetLayoutWindows();
|
||||
Assert::IsTrue(layoutWindows.GetZoneIndexSetFromWindow(window).empty());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -45,6 +45,14 @@ namespace FancyZonesEditor
|
||||
|
||||
protected void OnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// restore backup, clean up
|
||||
App.Overlay.EndEditing(true);
|
||||
|
||||
// select and draw applied layout
|
||||
var settings = ((App)Application.Current).MainWindowSettings;
|
||||
settings.SetSelectedModel(settings.AppliedModel);
|
||||
App.Overlay.CurrentDataContext = settings.AppliedModel;
|
||||
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,11 @@ namespace FancyZonesEditor
|
||||
|
||||
KeyUp += GridEditorWindow_KeyUp;
|
||||
KeyDown += ((App)Application.Current).App_KeyDown;
|
||||
|
||||
_stashedModel = (GridLayoutModel)(App.Overlay.CurrentDataContext as GridLayoutModel).Clone();
|
||||
}
|
||||
|
||||
protected new void OnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnCancel(sender, e);
|
||||
GridLayoutModel model = App.Overlay.CurrentDataContext as GridLayoutModel;
|
||||
_stashedModel.RestoreTo(model);
|
||||
}
|
||||
|
||||
private void GridEditorWindow_KeyUp(object sender, KeyEventArgs e)
|
||||
@ -37,8 +33,6 @@ namespace FancyZonesEditor
|
||||
((App)Application.Current).App_KeyUp(sender, e);
|
||||
}
|
||||
|
||||
private GridLayoutModel _stashedModel;
|
||||
|
||||
// This is required to fix a WPF rendering bug when using custom chrome
|
||||
private void EditorWindow_ContentRendered(object sender, System.EventArgs e)
|
||||
{
|
||||
|
@ -0,0 +1,69 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using FancyZonesEditor.Models;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
public class LayoutBackup
|
||||
{
|
||||
private LayoutModel _backup;
|
||||
private List<LayoutModel> _defaultLayoutsBackup;
|
||||
|
||||
public LayoutBackup()
|
||||
{
|
||||
}
|
||||
|
||||
public void Backup(LayoutModel model)
|
||||
{
|
||||
if (model is GridLayoutModel grid)
|
||||
{
|
||||
_backup = new GridLayoutModel(grid);
|
||||
}
|
||||
else if (model is CanvasLayoutModel canvas)
|
||||
{
|
||||
_backup = new CanvasLayoutModel(canvas);
|
||||
}
|
||||
|
||||
_defaultLayoutsBackup = new List<LayoutModel>(MainWindowSettingsModel.DefaultLayouts.Layouts);
|
||||
}
|
||||
|
||||
public void Restore()
|
||||
{
|
||||
if (_backup != null)
|
||||
{
|
||||
var settings = ((App)Application.Current).MainWindowSettings;
|
||||
var selectedModel = settings.SelectedModel;
|
||||
|
||||
if (selectedModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_backup is GridLayoutModel grid)
|
||||
{
|
||||
grid.RestoreTo((GridLayoutModel)selectedModel);
|
||||
grid.InitTemplateZones();
|
||||
}
|
||||
else if (_backup is CanvasLayoutModel canvas)
|
||||
{
|
||||
canvas.RestoreTo((CanvasLayoutModel)selectedModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (_defaultLayoutsBackup != null)
|
||||
{
|
||||
MainWindowSettingsModel.DefaultLayouts.Restore(_defaultLayoutsBackup);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_backup = null;
|
||||
_defaultLayoutsBackup = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,6 @@ namespace FancyZonesEditor
|
||||
private const int MinimalForDefaultWrapPanelsHeight = 900;
|
||||
|
||||
private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings;
|
||||
private LayoutModel _backup;
|
||||
private List<LayoutModel> _defaultLayoutsBackup;
|
||||
|
||||
private ContentDialog _openedDialog;
|
||||
private TextBlock _createLayoutAnnounce;
|
||||
@ -244,21 +242,15 @@ namespace FancyZonesEditor
|
||||
Logger.LogTrace();
|
||||
|
||||
var dataContext = ((FrameworkElement)sender).DataContext;
|
||||
Select((LayoutModel)dataContext);
|
||||
|
||||
EditLayoutDialog.Hide();
|
||||
|
||||
var mainEditor = App.Overlay;
|
||||
if (mainEditor.CurrentDataContext is not LayoutModel model)
|
||||
if (dataContext is not LayoutModel model)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
model.IsSelected = false;
|
||||
|
||||
// make a copy
|
||||
model = model.Clone();
|
||||
mainEditor.CurrentDataContext = model;
|
||||
|
||||
string name = model.Name;
|
||||
var index = name.LastIndexOf('(');
|
||||
@ -293,11 +285,8 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
model.Name = name + " (" + (++maxCustomIndex) + ')';
|
||||
|
||||
model.Persist();
|
||||
|
||||
App.Overlay.Monitors[App.Overlay.CurrentDesktop].SetLayoutSettings(model);
|
||||
App.FancyZonesEditorIO.SerializeAppliedLayouts();
|
||||
App.FancyZonesEditorIO.SerializeCustomLayouts();
|
||||
}
|
||||
|
||||
@ -327,7 +316,7 @@ namespace FancyZonesEditor
|
||||
private void OnClosing(object sender, EventArgs e)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
CancelLayoutChanges();
|
||||
App.Overlay.EndEditing(true);
|
||||
|
||||
App.FancyZonesEditorIO.SerializeAppliedLayouts();
|
||||
App.FancyZonesEditorIO.SerializeCustomLayouts();
|
||||
@ -356,17 +345,7 @@ namespace FancyZonesEditor
|
||||
|
||||
var dataContext = ((FrameworkElement)sender).DataContext;
|
||||
Select((LayoutModel)dataContext);
|
||||
|
||||
if (_settings.SelectedModel is GridLayoutModel grid)
|
||||
{
|
||||
_backup = new GridLayoutModel(grid);
|
||||
}
|
||||
else if (_settings.SelectedModel is CanvasLayoutModel canvas)
|
||||
{
|
||||
_backup = new CanvasLayoutModel(canvas);
|
||||
}
|
||||
|
||||
_defaultLayoutsBackup = new List<LayoutModel>(MainWindowSettingsModel.DefaultLayouts.Layouts);
|
||||
App.Overlay.StartEditing((LayoutModel)dataContext);
|
||||
|
||||
Keyboard.ClearFocus();
|
||||
EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Edit_Template, ((LayoutModel)dataContext).Name);
|
||||
@ -459,7 +438,7 @@ namespace FancyZonesEditor
|
||||
// EditLayout: Cancel changes
|
||||
private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
CancelLayoutChanges();
|
||||
App.Overlay.EndEditing(false);
|
||||
Select(_settings.AppliedModel);
|
||||
}
|
||||
|
||||
@ -474,8 +453,7 @@ namespace FancyZonesEditor
|
||||
return;
|
||||
}
|
||||
|
||||
_backup = null;
|
||||
_defaultLayoutsBackup = null;
|
||||
mainEditor.EndEditing(false);
|
||||
|
||||
// update current settings
|
||||
if (model == _settings.AppliedModel)
|
||||
@ -510,12 +488,6 @@ namespace FancyZonesEditor
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
LayoutModel model = element.DataContext as LayoutModel;
|
||||
|
||||
if (_backup != null && model.Guid == _backup.Guid)
|
||||
{
|
||||
_backup = null;
|
||||
}
|
||||
|
||||
MainWindowSettingsModel.DefaultLayouts.Reset(model.Uuid);
|
||||
|
||||
if (model == _settings.AppliedModel)
|
||||
@ -591,21 +563,6 @@ namespace FancyZonesEditor
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelLayoutChanges()
|
||||
{
|
||||
if (_backup != null)
|
||||
{
|
||||
_settings.RestoreSelectedModel(_backup);
|
||||
_backup = null;
|
||||
}
|
||||
|
||||
if (_defaultLayoutsBackup != null)
|
||||
{
|
||||
MainWindowSettingsModel.DefaultLayouts.Restore(_defaultLayoutsBackup);
|
||||
_defaultLayoutsBackup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void NumberBox_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// The TextBox inside a NumberBox doesn't inherit the Automation Properties name, so we have to set it.
|
||||
|
@ -267,27 +267,6 @@ namespace FancyZonesEditor
|
||||
return foundModel;
|
||||
}
|
||||
|
||||
public void RestoreSelectedModel(LayoutModel model)
|
||||
{
|
||||
if (SelectedModel == null || model == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedModel.SensitivityRadius = model.SensitivityRadius;
|
||||
SelectedModel.TemplateZoneCount = model.TemplateZoneCount;
|
||||
SelectedModel.IsSelected = model.IsSelected;
|
||||
SelectedModel.IsApplied = model.IsApplied;
|
||||
SelectedModel.Name = model.Name;
|
||||
SelectedModel.QuickKey = model.QuickKey;
|
||||
|
||||
if (model is GridLayoutModel grid)
|
||||
{
|
||||
((GridLayoutModel)SelectedModel).Spacing = grid.Spacing;
|
||||
((GridLayoutModel)SelectedModel).ShowSpacing = grid.ShowSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelectedModel(LayoutModel model)
|
||||
{
|
||||
if (_selectedModel != null)
|
||||
|
@ -17,6 +17,7 @@ namespace FancyZonesEditor
|
||||
private LayoutPreview _layoutPreview;
|
||||
private UserControl _editorLayout;
|
||||
private EditorWindow _editorWindow;
|
||||
private LayoutBackup _layoutBackup = new LayoutBackup();
|
||||
|
||||
public List<Monitor> Monitors { get; private set; }
|
||||
|
||||
@ -261,6 +262,21 @@ namespace FancyZonesEditor
|
||||
}
|
||||
}
|
||||
|
||||
public void StartEditing(LayoutModel model)
|
||||
{
|
||||
_layoutBackup.Backup(model);
|
||||
}
|
||||
|
||||
public void EndEditing(bool restoreBackup)
|
||||
{
|
||||
if (restoreBackup)
|
||||
{
|
||||
_layoutBackup.Restore();
|
||||
}
|
||||
|
||||
_layoutBackup.Clear();
|
||||
}
|
||||
|
||||
public void CloseLayoutWindow()
|
||||
{
|
||||
for (int i = 0; i < DesktopsCount; i++)
|
||||
|
@ -207,6 +207,8 @@ LRESULT KeyboardManagerEditor::KeyHookProc(int nCode, WPARAM wParam, LPARAM lPar
|
||||
{
|
||||
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
event.wParam = wParam;
|
||||
event.lParam->vkCode = Helpers::EncodeKeyNumpadOrigin(event.lParam->vkCode, event.lParam->flags & LLKHF_EXTENDED);
|
||||
|
||||
if (editor->HandleKeyboardHookEvent(&event) == 1)
|
||||
{
|
||||
// Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks
|
||||
|
@ -27,7 +27,7 @@ DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
|
||||
}
|
||||
|
||||
auto value = winrt::unbox_value<hstring>(dataContext);
|
||||
return stoi(std::wstring(value));
|
||||
return stoul(std::wstring(value));
|
||||
}
|
||||
|
||||
void KeyDropDownControl::SetSelectedValue(std::wstring value)
|
||||
|
@ -42,8 +42,7 @@ namespace KeyboardEventHandlers
|
||||
key_count = std::get<Shortcut>(it->second).Size();
|
||||
}
|
||||
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
|
||||
|
||||
// Handle remaps to VK_WIN_BOTH
|
||||
DWORD target;
|
||||
@ -148,7 +147,6 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
int key_count = 2;
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
|
||||
@ -232,8 +230,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// key down for all new shortcut keys except the common modifiers
|
||||
key_count = dest_size - commonKeys;
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
int i = 0;
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
@ -243,8 +240,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * static_cast<size_t>(commonKeys));
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
@ -282,8 +278,7 @@ namespace KeyboardEventHandlers
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
@ -302,7 +297,7 @@ namespace KeyboardEventHandlers
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii,static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), data->lParam->vkCode);
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,8 +356,7 @@ namespace KeyboardEventHandlers
|
||||
key_count += 1;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Release new shortcut state (release in reverse order of shortcut to be accurate)
|
||||
int i = 0;
|
||||
@ -400,8 +394,7 @@ namespace KeyboardEventHandlers
|
||||
key_count--;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
@ -453,8 +446,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
LPINPUT keyEventList = new INPUT[key_count]{};
|
||||
if (remapToShortcut)
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
@ -476,13 +468,12 @@ namespace KeyboardEventHandlers
|
||||
LPINPUT keyEventList;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// If remapped to disable, do nothing and suppress the key event
|
||||
// If remapped to disable, do nothing and suppress the key event
|
||||
// Since the original shortcut's action key is released, set it to false
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
return 1;
|
||||
@ -491,13 +482,12 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
|
||||
bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
|
||||
|
||||
|
||||
// If the keyboard state is clear, we release the target key but do not reset the remap state
|
||||
if (isKeyboardStateClear)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD,static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -507,8 +497,7 @@ namespace KeyboardEventHandlers
|
||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
@ -574,7 +563,7 @@ namespace KeyboardEventHandlers
|
||||
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList = nullptr;
|
||||
|
||||
|
||||
// Check if a new remapping should be applied
|
||||
Shortcut currentlyPressed = it->first;
|
||||
currentlyPressed.actionKey = data->lParam->vkCode;
|
||||
@ -588,8 +577,7 @@ namespace KeyboardEventHandlers
|
||||
DWORD to = std::get<0>(newRemapping.targetShortcut);
|
||||
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey));
|
||||
key_count = static_cast<size_t>(from.Size()) - 1 + 1 + (isLastKeyStillPressed ? 1 : 0);
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
int i = 0;
|
||||
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
|
||||
@ -599,7 +587,8 @@ namespace KeyboardEventHandlers
|
||||
i++;
|
||||
}
|
||||
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut to = std::get<Shortcut>(newRemapping.targetShortcut);
|
||||
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey));
|
||||
@ -608,7 +597,7 @@ namespace KeyboardEventHandlers
|
||||
temp_key_count_calculation += static_cast<size_t>(to.Size()) - 1;
|
||||
temp_key_count_calculation -= static_cast<size_t>(2) * from.GetCommonModifiersCount(to);
|
||||
key_count = temp_key_count_calculation + 1 + (isLastKeyStillPressed ? 1 : 0);
|
||||
keyEventList = new INPUT[key_count]();
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
int i = 0;
|
||||
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
|
||||
@ -635,7 +624,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
|
||||
// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
|
||||
key_count = (dest_size) + (src_size - 1) - (2 * static_cast<size_t>(commonKeys));
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
@ -646,8 +635,7 @@ namespace KeyboardEventHandlers
|
||||
key_count += 2;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Release new shortcut state (release in reverse order of shortcut to be accurate)
|
||||
int i = 0;
|
||||
@ -719,8 +707,7 @@ namespace KeyboardEventHandlers
|
||||
// Key down for original shortcut modifiers and action key, and current key press
|
||||
size_t key_count = src_size + 1;
|
||||
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
LPINPUT keyEventList = new INPUT[key_count]{};
|
||||
|
||||
// Set original shortcut key down state
|
||||
int i = 0;
|
||||
@ -730,7 +717,7 @@ namespace KeyboardEventHandlers
|
||||
if (isRemapToDisable && isOriginalActionKeyPressed)
|
||||
{
|
||||
// Set original action key
|
||||
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD,static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
@ -812,7 +799,7 @@ namespace KeyboardEventHandlers
|
||||
std::wstring query_string;
|
||||
|
||||
AppSpecificShortcutRemapTable::iterator it;
|
||||
|
||||
|
||||
// Check if an app-specific shortcut is already activated
|
||||
if (state.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
@ -854,8 +841,7 @@ namespace KeyboardEventHandlers
|
||||
if (Helpers::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH))
|
||||
{
|
||||
int key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
|
||||
|
||||
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
|
||||
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
||||
|
@ -66,13 +66,14 @@ void KeyboardManager::LoadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK KeyboardManager::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
LRESULT CALLBACK KeyboardManager::HookProc(int nCode, const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
LowlevelKeyboardEvent event;
|
||||
if (nCode == HC_ACTION)
|
||||
{
|
||||
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
event.wParam = wParam;
|
||||
event.lParam->vkCode = Helpers::EncodeKeyNumpadOrigin(event.lParam->vkCode, event.lParam->flags & LLKHF_EXTENDED);
|
||||
if (keyboardManagerObjectPtr->HandleKeyboardHookEvent(&event) == 1)
|
||||
{
|
||||
// Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks
|
||||
@ -83,7 +84,7 @@ LRESULT CALLBACK KeyboardManager::HookProc(int nCode, WPARAM wParam, LPARAM lPar
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return CallNextHookEx(hookHandleCopy, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,50 @@
|
||||
|
||||
namespace Helpers
|
||||
{
|
||||
DWORD EncodeKeyNumpadOrigin(const DWORD key, const bool extended)
|
||||
{
|
||||
bool numpad_originated = false;
|
||||
switch (key)
|
||||
{
|
||||
case VK_LEFT:
|
||||
case VK_RIGHT:
|
||||
case VK_UP:
|
||||
case VK_DOWN:
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
case VK_HOME:
|
||||
case VK_END:
|
||||
numpad_originated = !extended;
|
||||
break;
|
||||
case VK_RETURN:
|
||||
case VK_DIVIDE:
|
||||
numpad_originated = extended;
|
||||
break;
|
||||
}
|
||||
|
||||
if (numpad_originated)
|
||||
return key | GetNumpadOriginEncodingBit();
|
||||
else
|
||||
return key;
|
||||
}
|
||||
|
||||
DWORD ClearKeyNumpadOrigin(const DWORD key)
|
||||
{
|
||||
return (key & ~GetNumpadOriginEncodingBit());
|
||||
}
|
||||
|
||||
bool IsNumpadOriginated(const DWORD key)
|
||||
{
|
||||
return !!(key & GetNumpadOriginEncodingBit());
|
||||
}
|
||||
DWORD GetNumpadOriginEncodingBit()
|
||||
{
|
||||
// Intentionally do not mimic KF_EXTENDED to avoid confusion, because it's not the same thing
|
||||
// See EncodeKeyNumpadOrigin.
|
||||
return 1ull << 31;
|
||||
}
|
||||
// Function to check if the key is a modifier key
|
||||
bool IsModifierKey(DWORD key)
|
||||
{
|
||||
@ -18,7 +62,8 @@ namespace Helpers
|
||||
// Function to get the combined key for modifier keys
|
||||
DWORD GetCombinedKey(DWORD key)
|
||||
{
|
||||
switch (key) {
|
||||
switch (key)
|
||||
{
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
return CommonSharedConstants::VK_WIN_BOTH;
|
||||
|
@ -14,6 +14,12 @@ namespace Helpers
|
||||
Shift,
|
||||
Action
|
||||
};
|
||||
|
||||
// Functions to encode that a key is originated from numpad
|
||||
DWORD EncodeKeyNumpadOrigin(const DWORD key, const bool extended);
|
||||
DWORD ClearKeyNumpadOrigin(const DWORD key);
|
||||
bool IsNumpadOriginated(const DWORD key);
|
||||
DWORD GetNumpadOriginEncodingBit();
|
||||
|
||||
// Function to check if the key is a modifier key
|
||||
bool IsModifierKey(DWORD key);
|
||||
|
@ -194,7 +194,7 @@ DWORD Shortcut::GetShiftKey() const
|
||||
}
|
||||
|
||||
// Function to check if the input key matches the win key expected in the shortcut
|
||||
bool Shortcut::CheckWinKey(const DWORD& input) const
|
||||
bool Shortcut::CheckWinKey(const DWORD input) const
|
||||
{
|
||||
if (winKey == ModifierKey::Disabled)
|
||||
{
|
||||
@ -216,7 +216,7 @@ bool Shortcut::CheckWinKey(const DWORD& input) const
|
||||
}
|
||||
|
||||
// Function to check if the input key matches the ctrl key expected in the shortcut
|
||||
bool Shortcut::CheckCtrlKey(const DWORD& input) const
|
||||
bool Shortcut::CheckCtrlKey(const DWORD input) const
|
||||
{
|
||||
if (ctrlKey == ModifierKey::Disabled)
|
||||
{
|
||||
@ -238,7 +238,7 @@ bool Shortcut::CheckCtrlKey(const DWORD& input) const
|
||||
}
|
||||
|
||||
// Function to check if the input key matches the alt key expected in the shortcut
|
||||
bool Shortcut::CheckAltKey(const DWORD& input) const
|
||||
bool Shortcut::CheckAltKey(const DWORD input) const
|
||||
{
|
||||
if (altKey == ModifierKey::Disabled)
|
||||
{
|
||||
@ -260,7 +260,7 @@ bool Shortcut::CheckAltKey(const DWORD& input) const
|
||||
}
|
||||
|
||||
// Function to check if the input key matches the shift key expected in the shortcut
|
||||
bool Shortcut::CheckShiftKey(const DWORD& input) const
|
||||
bool Shortcut::CheckShiftKey(const DWORD input) const
|
||||
{
|
||||
if (shiftKey == ModifierKey::Disabled)
|
||||
{
|
||||
@ -282,7 +282,7 @@ bool Shortcut::CheckShiftKey(const DWORD& input) const
|
||||
}
|
||||
|
||||
// Function to set a key in the shortcut based on the passed key code argument. Returns false if it is already set to the same value. This can be used to avoid UI refreshing
|
||||
bool Shortcut::SetKey(const DWORD& input)
|
||||
bool Shortcut::SetKey(const DWORD input)
|
||||
{
|
||||
// Since there isn't a key for a common Win key we use the key code defined by us
|
||||
if (input == CommonSharedConstants::VK_WIN_BOTH)
|
||||
@ -394,7 +394,7 @@ bool Shortcut::SetKey(const DWORD& input)
|
||||
}
|
||||
|
||||
// Function to reset the state of a shortcut key based on the passed key code argument. Since there is no VK_WIN code, use the second argument for setting common win key.
|
||||
void Shortcut::ResetKey(const DWORD& input)
|
||||
void Shortcut::ResetKey(const DWORD input)
|
||||
{
|
||||
// Since there isn't a key for a common Win key this is handled with a separate argument.
|
||||
if (input == CommonSharedConstants::VK_WIN_BOTH || input == VK_LWIN || input == VK_RWIN)
|
||||
@ -415,7 +415,7 @@ void Shortcut::ResetKey(const DWORD& input)
|
||||
}
|
||||
else
|
||||
{
|
||||
actionKey = NULL;
|
||||
actionKey = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include "ModifierKey.h"
|
||||
|
||||
#include <compare>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace KeyboardManagerInput
|
||||
@ -14,91 +17,34 @@ private:
|
||||
// Function to split a wstring based on a delimiter and return a vector of split strings
|
||||
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);
|
||||
|
||||
public:
|
||||
ModifierKey winKey;
|
||||
ModifierKey ctrlKey;
|
||||
ModifierKey altKey;
|
||||
ModifierKey shiftKey;
|
||||
DWORD actionKey;
|
||||
|
||||
// By default create an empty shortcut
|
||||
Shortcut() :
|
||||
winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL)
|
||||
inline auto comparator() const
|
||||
{
|
||||
return std::make_tuple(winKey, ctrlKey, altKey, shiftKey, actionKey);
|
||||
}
|
||||
|
||||
public:
|
||||
ModifierKey winKey = ModifierKey::Disabled;
|
||||
ModifierKey ctrlKey = ModifierKey::Disabled;
|
||||
ModifierKey altKey = ModifierKey::Disabled;
|
||||
ModifierKey shiftKey = ModifierKey::Disabled;
|
||||
DWORD actionKey = {};
|
||||
|
||||
Shortcut() = default;
|
||||
|
||||
// Constructor to initialize Shortcut from it's virtual key code string representation.
|
||||
Shortcut(const std::wstring& shortcutVK);
|
||||
|
||||
// Constructor to initialize shortcut from a list of keys
|
||||
Shortcut(const std::vector<int32_t>& keys);
|
||||
|
||||
// == operator
|
||||
inline bool operator==(const Shortcut& sc) const
|
||||
inline friend auto operator<=>(const Shortcut& lhs, const Shortcut& rhs) noexcept
|
||||
{
|
||||
return (winKey == sc.winKey && ctrlKey == sc.ctrlKey && altKey == sc.altKey && shiftKey == sc.shiftKey && actionKey == sc.actionKey);
|
||||
return lhs.comparator() <=> rhs.comparator();
|
||||
}
|
||||
|
||||
// Less than operator must be defined to use with std::map.
|
||||
inline bool operator<(const Shortcut& sc) const
|
||||
inline friend bool operator==(const Shortcut& lhs, const Shortcut& rhs) noexcept
|
||||
{
|
||||
// Compare win key first
|
||||
if (winKey < sc.winKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (winKey > sc.winKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If win key is equal, then compare ctrl key
|
||||
if (ctrlKey < sc.ctrlKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ctrlKey > sc.ctrlKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If ctrl key is equal, then compare alt key
|
||||
if (altKey < sc.altKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (altKey > sc.altKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If alt key is equal, then compare shift key
|
||||
if (shiftKey < sc.shiftKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (shiftKey > sc.shiftKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If shift key is equal, then compare action key
|
||||
if (actionKey < sc.actionKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lhs.comparator() == rhs.comparator();
|
||||
}
|
||||
|
||||
// Function to return the number of keys in the shortcut
|
||||
@ -126,22 +72,22 @@ public:
|
||||
DWORD GetShiftKey() const;
|
||||
|
||||
// Function to check if the input key matches the win key expected in the shortcut
|
||||
bool CheckWinKey(const DWORD& input) const;
|
||||
bool CheckWinKey(const DWORD input) const;
|
||||
|
||||
// Function to check if the input key matches the ctrl key expected in the shortcut
|
||||
bool CheckCtrlKey(const DWORD& input) const;
|
||||
bool CheckCtrlKey(const DWORD input) const;
|
||||
|
||||
// Function to check if the input key matches the alt key expected in the shortcut
|
||||
bool CheckAltKey(const DWORD& input) const;
|
||||
bool CheckAltKey(const DWORD input) const;
|
||||
|
||||
// Function to check if the input key matches the shift key expected in the shortcut
|
||||
bool CheckShiftKey(const DWORD& input) const;
|
||||
bool CheckShiftKey(const DWORD input) const;
|
||||
|
||||
// Function to set a key in the shortcut based on the passed key code argument. Returns false if it is already set to the same value. This can be used to avoid UI refreshing
|
||||
bool SetKey(const DWORD& input);
|
||||
bool SetKey(const DWORD input);
|
||||
|
||||
// Function to reset the state of a shortcut key based on the passed key code argument
|
||||
void ResetKey(const DWORD& input);
|
||||
void ResetKey(const DWORD input);
|
||||
|
||||
// Function to return the string representation of the shortcut in virtual key codes appended in a string by ";" separator.
|
||||
winrt::hstring ToHstringVK() const;
|
||||
|
@ -29,6 +29,7 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
|
||||
[DataRow("base64 abc", typeof(Base64.Base64Request))]
|
||||
[DataRow("base99 abc", null)]
|
||||
[DataRow("base64s abc", null)]
|
||||
[DataRow("base64d abc=", typeof(Base64.Base64DecodeRequest))]
|
||||
public void ParserTest(string input, Type? expectedRequestType)
|
||||
{
|
||||
var parser = new InputParser();
|
||||
@ -77,7 +78,7 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
|
||||
|
||||
private static bool CommandIsKnown(string command)
|
||||
{
|
||||
string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64" };
|
||||
string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64", "base64d" };
|
||||
|
||||
if (hashes.Contains(command.ToLowerInvariant()))
|
||||
{
|
||||
|
@ -0,0 +1,58 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Wox.Plugin.Logger;
|
||||
|
||||
namespace Community.PowerToys.Run.Plugin.ValueGenerator.Base64
|
||||
{
|
||||
public class Base64DecodeRequest : IComputeRequest
|
||||
{
|
||||
public byte[] Result { get; set; }
|
||||
|
||||
public string Description => "Base64 Decoding";
|
||||
|
||||
public bool IsSuccessful { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
private string DataToDecode { get; set; }
|
||||
|
||||
public Base64DecodeRequest(string dataToDecode)
|
||||
{
|
||||
DataToDecode = dataToDecode ?? throw new ArgumentNullException(nameof(dataToDecode));
|
||||
}
|
||||
|
||||
public bool Compute()
|
||||
{
|
||||
IsSuccessful = true;
|
||||
try
|
||||
{
|
||||
Result = System.Convert.FromBase64String(DataToDecode);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(e.Message, e, GetType());
|
||||
ErrorMessage = e.Message;
|
||||
IsSuccessful = false;
|
||||
}
|
||||
|
||||
return IsSuccessful;
|
||||
}
|
||||
|
||||
public string ResultToString()
|
||||
{
|
||||
if (Result != null)
|
||||
{
|
||||
return Encoding.UTF8.GetString(Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -121,6 +121,12 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator
|
||||
string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
|
||||
request = new Base64Request(Encoding.UTF8.GetBytes(content));
|
||||
}
|
||||
else if (command.ToLower(null) == "base64d")
|
||||
{
|
||||
int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
|
||||
string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
|
||||
request = new Base64DecodeRequest(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException($"Invalid Query: {query.RawUserQuery}");
|
||||
|
@ -13,6 +13,8 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.Plugin.Shell.Properties;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Wox.Infrastructure.Storage;
|
||||
using Wox.Plugin;
|
||||
using Wox.Plugin.Common;
|
||||
@ -21,7 +23,7 @@ using Control = System.Windows.Controls.Control;
|
||||
|
||||
namespace Microsoft.Plugin.Shell
|
||||
{
|
||||
public class Main : IPlugin, IPluginI18n, IContextMenu, ISavable
|
||||
public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavable
|
||||
{
|
||||
private static readonly IFileSystem FileSystem = new FileSystem();
|
||||
private static readonly IPath Path = FileSystem.Path;
|
||||
@ -37,6 +39,16 @@ namespace Microsoft.Plugin.Shell
|
||||
|
||||
public string Description => Properties.Resources.wox_plugin_cmd_plugin_description;
|
||||
|
||||
public IEnumerable<PluginAdditionalOption> AdditionalOptions => new List<PluginAdditionalOption>()
|
||||
{
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = "LeaveShellOpen",
|
||||
DisplayLabel = Resources.wox_leave_shell_open,
|
||||
Value = _settings.LeaveShellOpen,
|
||||
},
|
||||
};
|
||||
|
||||
private PluginInitContext _context;
|
||||
|
||||
public Main()
|
||||
@ -217,17 +229,41 @@ namespace Microsoft.Plugin.Shell
|
||||
if (ExistInPath(filename))
|
||||
{
|
||||
var arguments = parts[1];
|
||||
info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsVerbArg);
|
||||
if (_settings.LeaveShellOpen)
|
||||
{
|
||||
// Wrap the command in a cmd.exe process
|
||||
info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{filename} {arguments}\"", runAsVerbArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsVerbArg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_settings.LeaveShellOpen)
|
||||
{
|
||||
// Wrap the command in a cmd.exe process
|
||||
info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{command}\"", runAsVerbArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_settings.LeaveShellOpen)
|
||||
{
|
||||
// Wrap the command in a cmd.exe process
|
||||
info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{command}\"", runAsVerbArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -375,5 +411,19 @@ namespace Microsoft.Plugin.Shell
|
||||
|
||||
return resultlist;
|
||||
}
|
||||
|
||||
public void UpdateSettings(PowerLauncherPluginSettings settings)
|
||||
{
|
||||
var leaveShellOpen = false;
|
||||
|
||||
if (settings != null && settings.AdditionalOptions != null)
|
||||
{
|
||||
var optionLeaveShellOpen = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "LeaveShellOpen");
|
||||
leaveShellOpen = optionLeaveShellOpen?.Value ?? leaveShellOpen;
|
||||
_settings.LeaveShellOpen = leaveShellOpen;
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,15 @@ namespace Microsoft.Plugin.Shell.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Keep shell open.
|
||||
/// </summary>
|
||||
public static string wox_leave_shell_open {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_leave_shell_open", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to this command has been executed {0} times.
|
||||
/// </summary>
|
||||
|
@ -141,4 +141,7 @@
|
||||
<data name="wox_plugin_cmd_run_as_user" xml:space="preserve">
|
||||
<value>Run as different user (Ctrl+Shift+U)</value>
|
||||
</data>
|
||||
<data name="wox_leave_shell_open" xml:space="preserve">
|
||||
<value>Keep shell open</value>
|
||||
</data>
|
||||
</root>
|
@ -17,7 +17,9 @@ namespace Peek.Common.Helpers
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
List<string> format = new List<string>
|
||||
{
|
||||
resourceLoader.GetString("ReadableString_ByteAbbreviationFormat"), // "B"
|
||||
(bytes == 1) ?
|
||||
resourceLoader.GetString("ReadableString_ByteAbbreviationFormat") : // "byte"
|
||||
resourceLoader.GetString("ReadableString_BytesAbbreviationFormat"), // "bytes"
|
||||
resourceLoader.GetString("ReadableString_KiloByteAbbreviationFormat"), // "KB"
|
||||
resourceLoader.GetString("ReadableString_MegaByteAbbreviationFormat"), // "MB"
|
||||
resourceLoader.GetString("ReadableString_GigaByteAbbreviationFormat"), // "GB"
|
||||
|
@ -12,9 +12,15 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<controls:WebView2 x:Name="PreviewBrowser"
|
||||
Loaded="PreviewWV2_Loaded"
|
||||
NavigationStarting="PreviewBrowser_NavigationStarting"
|
||||
NavigationCompleted="PreviewWV2_NavigationCompleted"/>
|
||||
<controls:WebView2
|
||||
x:Name="PreviewBrowser"
|
||||
Loaded="PreviewWV2_Loaded"
|
||||
NavigationStarting="PreviewBrowser_NavigationStarting"
|
||||
NavigationCompleted="PreviewWV2_NavigationCompleted" />
|
||||
<ContentDialog
|
||||
x:Name="OpenUriDialog"
|
||||
x:Uid="OpenUriDialog"
|
||||
PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"
|
||||
SecondaryButtonClick="OpenUriDialog_SecondaryButtonClick" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
@ -3,11 +3,13 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Peek.Common.Constants;
|
||||
using Peek.Common.Helpers;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
|
||||
@ -96,6 +98,7 @@ namespace Peek.FilePreviewer.Controls
|
||||
|
||||
private void SourcePropertyChanged()
|
||||
{
|
||||
OpenUriDialog.Hide();
|
||||
Navigate();
|
||||
}
|
||||
|
||||
@ -135,6 +138,7 @@ namespace Peek.FilePreviewer.Controls
|
||||
}
|
||||
|
||||
PreviewBrowser.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
||||
PreviewBrowser.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -149,6 +153,16 @@ namespace Peek.FilePreviewer.Controls
|
||||
DOMContentLoaded?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
private async void CoreWebView2_NewWindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
|
||||
{
|
||||
// Monaco opens URI in a new window. We open the URI in the default web browser.
|
||||
if (args.Uri != null && args.IsUserInitiated)
|
||||
{
|
||||
args.Handled = true;
|
||||
await ShowOpenUriDialogAsync(new Uri(args.Uri));
|
||||
}
|
||||
}
|
||||
|
||||
private async void PreviewBrowser_NavigationStarting(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
if (_navigatedUri == null)
|
||||
@ -157,10 +171,11 @@ namespace Peek.FilePreviewer.Controls
|
||||
}
|
||||
|
||||
// In case user starts or tries to navigate from within the HTML file we launch default web browser for navigation.
|
||||
if (args.Uri != null && args.Uri != _navigatedUri?.ToString() && args.IsUserInitiated)
|
||||
// TODO: && args.IsUserInitiated - always false for PDF files, revert the workaround when fixed in WebView2: https://github.com/microsoft/PowerToys/issues/27403
|
||||
if (args.Uri != null && args.Uri != _navigatedUri?.ToString())
|
||||
{
|
||||
args.Cancel = true;
|
||||
await Launcher.LaunchUriAsync(new Uri(args.Uri));
|
||||
await ShowOpenUriDialogAsync(new Uri(args.Uri));
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +186,29 @@ namespace Peek.FilePreviewer.Controls
|
||||
_navigatedUri = Source;
|
||||
}
|
||||
|
||||
NavigationCompleted?.Invoke(sender, args);
|
||||
// Don't raise NavigationCompleted event if NavigationStarting has been cancelled
|
||||
if (args.WebErrorStatus != CoreWebView2WebErrorStatus.OperationCanceled)
|
||||
{
|
||||
NavigationCompleted?.Invoke(sender, args);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowOpenUriDialogAsync(Uri uri)
|
||||
{
|
||||
OpenUriDialog.Content = uri.ToString();
|
||||
var result = await OpenUriDialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenUriDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(sender.Content.ToString());
|
||||
Clipboard.SetContent(dataPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@
|
||||
<comment>Date Modified label for the unsupported files view. {0} is the date.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_ByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} bytes</value>
|
||||
<value>{0} byte</value>
|
||||
<comment>Abbreviation for the size unit byte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_KiloByteAbbreviationFormat" xml:space="preserve">
|
||||
@ -233,4 +233,24 @@
|
||||
<value>{0} (extracted {1})</value>
|
||||
<comment>{0} is the size of the archive, {1} is the extracted size</comment>
|
||||
</data>
|
||||
<data name="ReadableString_BytesAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} bytes</value>
|
||||
<comment>Abbreviation for the size bytes</comment>
|
||||
</data>
|
||||
<data name="OpenUriDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
<comment>Dialog showed when an URI is clicked. Button to close the dialog.</comment>
|
||||
</data>
|
||||
<data name="OpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
<comment>Dialog showed when an URI is clicked. Button to open the URI.</comment>
|
||||
</data>
|
||||
<data name="OpenUriDialog.SecondaryButtonText" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
<comment>Dialog showed when an URI is clicked. Button to copy the URI.</comment>
|
||||
</data>
|
||||
<data name="OpenUriDialog.Title" xml:space="preserve">
|
||||
<value>Do you want Peek to open the external application?</value>
|
||||
<comment>Title of the dialog showed when an URI is clicked,"Peek" is the name of the utility. </comment>
|
||||
</data>
|
||||
</root>
|
@ -503,6 +503,7 @@
|
||||
<ToggleButton
|
||||
x:Name="toggleButton_enumItems"
|
||||
x:Uid="ToggleButton_EnumItems"
|
||||
IsChecked="True"
|
||||
Height="32"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}" />
|
||||
|
@ -157,6 +157,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
|
||||
_webView.NavigationCompleted += WebView2Init;
|
||||
_webView.Height = this.Height;
|
||||
_webView.Width = this.Width;
|
||||
_webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
|
||||
Controls.Add(_webView);
|
||||
_webView.SendToBack();
|
||||
_loadingBar.Value = 100;
|
||||
@ -218,6 +219,16 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
|
||||
}
|
||||
}
|
||||
|
||||
private async void CoreWebView2_NewWindowRequested(object sender, CoreWebView2NewWindowRequestedEventArgs e)
|
||||
{
|
||||
// Monaco opens URI in a new window. We open the URI in the default web browser.
|
||||
if (e.Uri != null && e.IsUserInitiated)
|
||||
{
|
||||
e.Handled = true;
|
||||
await Launcher.LaunchUriAsync(new Uri(e.Uri));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event sets the height and width of the webview to the size of the form
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user