Add keyboard shortcuts (without GUI) for switching windows in the same zone (tabs) (#13973)

Authored-by: float4 <float4-unspecified-mail>
This commit is contained in:
FLOAT4 2021-11-03 17:11:42 +02:00 committed by GitHub
parent cd5c22aaa1
commit 9d9df949ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 507 additions and 46 deletions

View file

@ -147,7 +147,8 @@ protected:
private:
void UpdateZoneWindows() noexcept;
void UpdateWindowsPositions() noexcept;
void UpdateWindowsPositions(bool suppressMove = false) noexcept;
void CycleTabs(bool reverse) noexcept;
bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkey(DWORD vkCode) noexcept;
@ -155,6 +156,7 @@ private:
void RegisterVirtualDesktopUpdates() noexcept;
void UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept;
void OnSettingsChanged() noexcept;
std::pair<winrt::com_ptr<IWorkArea>, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map<HMONITOR, winrt::com_ptr<IWorkArea>>& workAreaMap) noexcept;
@ -201,6 +203,14 @@ private:
Exit,
Terminate
};
// IDs used to register hot keys (keyboard shortcuts).
enum class HotkeyId : int
{
Editor = 1,
NextTab = 2,
PrevTab = 3,
};
};
std::function<void()> FancyZones::disableModuleCallback = {};
@ -224,7 +234,12 @@ FancyZones::Run() noexcept
return;
}
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
RegisterHotKey(m_window, static_cast<int>(HotkeyId::Editor), m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
if (m_settings->GetSettings()->windowSwitching)
{
RegisterHotKey(m_window, static_cast<int>(HotkeyId::NextTab), m_settings->GetSettings()->nextTabHotkey.get_modifiers(), m_settings->GetSettings()->nextTabHotkey.get_code());
RegisterHotKey(m_window, static_cast<int>(HotkeyId::PrevTab), m_settings->GetSettings()->prevTabHotkey.get_modifiers(), m_settings->GetSettings()->prevTabHotkey.get_code());
}
m_virtualDesktop.Init();
@ -639,10 +654,15 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
{
case WM_HOTKEY:
{
if (wparam == 1)
if (wparam == static_cast<WPARAM>(HotkeyId::Editor))
{
ToggleEditor();
}
else if (wparam == static_cast<WPARAM>(HotkeyId::NextTab) || wparam == static_cast<WPARAM>(HotkeyId::PrevTab))
{
bool reverse = wparam == static_cast<WPARAM>(HotkeyId::PrevTab);
CycleTabs(reverse);
}
}
break;
@ -787,6 +807,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
UpdateZoneWindows();
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
{
if (m_settings->GetSettings()->displayChange_moveWindows)
@ -897,7 +918,7 @@ void FancyZones::UpdateZoneWindows() noexcept
}
}
void FancyZones::UpdateWindowsPositions() noexcept
void FancyZones::UpdateWindowsPositions(bool suppressMove) noexcept
{
for (const auto [window, desktopId] : m_virtualDesktop.GetWindowsRelatedToDesktops())
{
@ -905,11 +926,23 @@ void FancyZones::UpdateWindowsPositions() noexcept
auto zoneWindow = m_workAreaHandler.GetWorkArea(window, desktopId);
if (zoneWindow)
{
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow, suppressMove);
}
}
}
void FancyZones::CycleTabs(bool reverse) noexcept
{
auto window = GetForegroundWindow();
HMONITOR current = WorkAreaKeyFromWindow(window);
auto workArea = m_workAreaHandler.GetWorkArea(m_currentDesktopId, current);
if (workArea)
{
workArea->CycleTabs(window, reverse);
}
}
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
{
_TRACER_;
@ -1135,21 +1168,36 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
}
void FancyZones::OnSettingsChanged() noexcept
void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept
{
_TRACER_;
m_settings->ReloadSettings();
UnregisterHotKey(m_window, hotkeyId);
// Update the hotkey
UnregisterHotKey(m_window, 1);
auto modifiers = m_settings->GetSettings()->editorHotkey.get_modifiers();
auto code = m_settings->GetSettings()->editorHotkey.get_code();
auto result = RegisterHotKey(m_window, 1, modifiers, code);
if (!enable)
{
return;
}
auto modifiers = hotkeyObject.get_modifiers();
auto code = hotkeyObject.get_code();
auto result = RegisterHotKey(m_window, hotkeyId, modifiers, code);
if (!result)
{
Logger::error(L"Failed to register hotkey: {}", get_last_error_or_default(GetLastError()));
}
}
void FancyZones::OnSettingsChanged() noexcept
{
_TRACER_;
m_settings->ReloadSettings();
// Update the hotkeys
UpdateHotkey(static_cast<int>(HotkeyId::Editor), m_settings->GetSettings()->editorHotkey, true);
auto windowSwitching = m_settings->GetSettings()->windowSwitching;
UpdateHotkey(static_cast<int>(HotkeyId::NextTab), m_settings->GetSettings()->nextTabHotkey, windowSwitching);
UpdateHotkey(static_cast<int>(HotkeyId::PrevTab), m_settings->GetSettings()->prevTabHotkey, windowSwitching);
// Needed if we toggled spanZonesAcrossMonitors
m_workAreaHandler.Clear();
@ -1177,10 +1225,9 @@ void FancyZones::UpdateZoneSets() noexcept
{
workArea->UpdateActiveZoneSet();
}
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
{
UpdateWindowsPositions();
}
auto moveWindows = m_settings->GetSettings()->zoneSetChange_moveWindows;
UpdateWindowsPositions(!moveWindows);
}
bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept

View file

@ -1,6 +1,7 @@
#pragma once
#include <vector>
#include <optional>
#include <FancyZonesLib/Zone.h>
@ -8,6 +9,7 @@
namespace ZonedWindowProperties
{
const wchar_t PropertyMultipleZoneID[] = L"FancyZones_zones";
const wchar_t PropertySortKeyWithinZone[] = L"FancyZones_TabSortKeyWithinZone";
const wchar_t PropertyRestoreSizeID[] = L"FancyZones_RestoreSize";
const wchar_t PropertyRestoreOriginID[] = L"FancyZones_RestoreOrigin";
@ -44,3 +46,28 @@ inline void StampWindow(HWND window, Bitmask bitmask) noexcept
memcpy(&rawData, data.data(), sizeof data);
SetProp(window, ZonedWindowProperties::PropertyMultipleZoneID, rawData);
}
inline std::optional<size_t> GetTabSortKeyWithinZone(HWND window)
{
auto rawTabSortKeyWithinZone = ::GetPropW(window, ZonedWindowProperties::PropertySortKeyWithinZone);
if (rawTabSortKeyWithinZone == NULL)
{
return std::nullopt;
}
auto tabSortKeyWithinZone = reinterpret_cast<uint64_t>(rawTabSortKeyWithinZone) - 1;
return tabSortKeyWithinZone;
}
inline void SetTabSortKeyWithinZone(HWND window, std::optional<size_t> tabSortKeyWithinZone)
{
if (!tabSortKeyWithinZone.has_value())
{
::RemovePropW(window, ZonedWindowProperties::PropertySortKeyWithinZone);
}
else
{
auto rawTabSortKeyWithinZone = reinterpret_cast<HANDLE>(tabSortKeyWithinZone.value() + 1);
::SetPropW(window, ZonedWindowProperties::PropertySortKeyWithinZone, rawTabSortKeyWithinZone);
}
}

View file

@ -196,6 +196,15 @@
<data name="Setting_Launch_Editor_Hotkey_Label" xml:space="preserve">
<value>Configure the zone editor hotkey</value>
</data>
<data name="Setting_Window_Switching_Toggle_Label" xml:space="preserve">
<value>Toggle shortcuts for switching between windows in the current zone</value>
</data>
<data name="Setting_Next_Tab_Hotkey_Label" xml:space="preserve">
<value>Shortcut for switching to the next window in the current zone</value>
</data>
<data name="Setting_Prev_Tab_Hotkey_Label" xml:space="preserve">
<value>Shortcut for switching to the previous window in the current zone</value>
</data>
<data name="Setting_Excluded_Apps_Description" xml:space="preserve">
<value>To exclude an application from snapping to zones add its name here (one per line). Excluded apps will react to the Windows Snap regardless of all other settings.</value>
<comment>Windows refers to the Operating system</comment>

View file

@ -32,6 +32,9 @@ namespace NonLocalizable
const wchar_t ZoneBorderColorID[] = L"fancyzones_zoneBorderColor";
const wchar_t ZoneHighlightColorID[] = L"fancyzones_zoneHighlightColor";
const wchar_t EditorHotkeyID[] = L"fancyzones_editor_hotkey";
const wchar_t WindowSwitchingToggleID[] = L"fancyzones_windowSwitching";
const wchar_t NextTabHotkeyID[] = L"fancyzones_nextTab_hotkey";
const wchar_t PrevTabHotkeyID[] = L"fancyzones_prevTab_hotkey";
const wchar_t ExcludedAppsID[] = L"fancyzones_excluded_apps";
const wchar_t ZoneHighlightOpacityID[] = L"fancyzones_highlight_opacity";
@ -77,7 +80,7 @@ private:
PCWSTR name;
bool* value;
int resourceId;
} m_configBools[16] = {
} m_configBools[17] = {
{ NonLocalizable::ShiftDragID, &m_settings.shiftDrag, IDS_SETTING_DESCRIPTION_SHIFTDRAG },
{ NonLocalizable::MouseSwitchID, &m_settings.mouseSwitch, IDS_SETTING_DESCRIPTION_MOUSESWITCH },
{ NonLocalizable::OverrideSnapHotKeysID, &m_settings.overrideSnapHotkeys, IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS },
@ -94,6 +97,7 @@ private:
{ NonLocalizable::ShowOnAllMonitorsID, &m_settings.showZonesOnAllMonitors, IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS },
{ NonLocalizable::SpanZonesAcrossMonitorsID, &m_settings.spanZonesAcrossMonitors, IDS_SETTING_DESCRIPTION_SPAN_ZONES_ACROSS_MONITORS },
{ NonLocalizable::MakeDraggedWindowTransparentID, &m_settings.makeDraggedWindowTransparent, IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT },
{ NonLocalizable::WindowSwitchingToggleID, &m_settings.windowSwitching, IDS_SETTING_WINDOW_SWITCHING_TOGGLE_LABEL },
};
};
@ -116,6 +120,8 @@ FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ int* buffer_size) noexce
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
settings.add_hotkey(NonLocalizable::EditorHotkeyID, IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, m_settings.editorHotkey);
settings.add_hotkey(NonLocalizable::NextTabHotkeyID, IDS_SETTING_NEXT_TAB_HOTKEY_LABEL, m_settings.nextTabHotkey);
settings.add_hotkey(NonLocalizable::PrevTabHotkeyID, IDS_SETTING_PREV_TAB_HOTKEY_LABEL, m_settings.prevTabHotkey);
for (auto const& setting : m_configBools)
{
@ -182,6 +188,16 @@ void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept
m_settings.editorHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(NonLocalizable::NextTabHotkeyID))
{
m_settings.nextTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(NonLocalizable::PrevTabHotkeyID))
{
m_settings.prevTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (auto val = values.get_string_value(NonLocalizable::ExcludedAppsID))
{
m_settings.excludedApps = std::move(*val);
@ -246,6 +262,8 @@ void FancyZonesSettings::SaveSettings() noexcept
values.add_property(NonLocalizable::ZoneHighlightOpacityID, m_settings.zoneHighlightOpacity);
values.add_property(NonLocalizable::OverlappingZonesAlgorithmID, (int)m_settings.overlappingZonesAlgorithm);
values.add_property(NonLocalizable::EditorHotkeyID, m_settings.editorHotkey.get_json());
values.add_property(NonLocalizable::NextTabHotkeyID, m_settings.nextTabHotkey.get_json());
values.add_property(NonLocalizable::PrevTabHotkeyID, m_settings.prevTabHotkey.get_json());
values.add_property(NonLocalizable::ExcludedAppsID, m_settings.excludedApps);
values.save_to_settings_file();

View file

@ -38,6 +38,9 @@ struct Settings
int zoneHighlightOpacity = 50;
OverlappingZonesAlgorithm overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
PowerToysSettings::HotkeyObject editorHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, VK_OEM_3);
bool windowSwitching = true;
PowerToysSettings::HotkeyObject nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_NEXT);
PowerToysSettings::HotkeyObject prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_PRIOR);
std::wstring excludedApps = L"";
std::vector<std::wstring> excludedAppsArray;
};

View file

@ -123,6 +123,17 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const
}
}
}
auto zoneWindow = zoneWindowMap.find(monitor);
if (zoneWindow != zoneWindowMap.end())
{
const auto zoneWindowPtr = zoneWindow->second;
const auto activeZoneSet = zoneWindowPtr->ActiveZoneSet();
if (activeZoneSet)
{
activeZoneSet->DismissWindow(window);
}
}
}
void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map<HMONITOR, winrt::com_ptr<IWorkArea>>& zoneWindowMap) noexcept
@ -273,11 +284,11 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st
}
}
void WindowMoveHandler::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, winrt::com_ptr<IWorkArea> zoneWindow) noexcept
void WindowMoveHandler::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, winrt::com_ptr<IWorkArea> zoneWindow, bool suppressMove) noexcept
{
if (window != m_windowMoveSize)
{
zoneWindow->MoveWindowIntoZoneByIndexSet(window, indexSet);
zoneWindow->MoveWindowIntoZoneByIndexSet(window, indexSet, suppressMove);
}
}

View file

@ -18,7 +18,7 @@ public:
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map<HMONITOR, winrt::com_ptr<IWorkArea>>& zoneWindowMap) noexcept;
void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::unordered_map<HMONITOR, winrt::com_ptr<IWorkArea>>& zoneWindowMap) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, winrt::com_ptr<IWorkArea> zoneWindow) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, winrt::com_ptr<IWorkArea> zoneWindow, bool suppressMove = false) noexcept;
bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, winrt::com_ptr<IWorkArea> zoneWindow) noexcept;
bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle, winrt::com_ptr<IWorkArea> zoneWindow) noexcept;
bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode, winrt::com_ptr<IWorkArea> zoneWindow) noexcept;

View file

@ -117,7 +117,7 @@ public:
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet) noexcept;
MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool suppressMove = false) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(bool)
@ -139,6 +139,8 @@ public:
IFACEMETHODIMP_(void)
UpdateActiveZoneSet() noexcept;
IFACEMETHODIMP_(void)
CycleTabs(HWND window, bool reverse) noexcept;
IFACEMETHODIMP_(void)
ClearSelectedZones() noexcept;
IFACEMETHODIMP_(void)
FlashZones() noexcept;
@ -311,11 +313,11 @@ WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept
}
IFACEMETHODIMP_(void)
WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet) noexcept
WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool suppressMove) noexcept
{
if (m_activeZoneSet)
{
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, indexSet);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, indexSet, suppressMove);
}
}
@ -431,6 +433,15 @@ WorkArea::UpdateActiveZoneSet() noexcept
}
}
IFACEMETHODIMP_(void)
WorkArea::CycleTabs(HWND window, bool reverse) noexcept
{
if (m_activeZoneSet)
{
m_activeZoneSet->CycleTabs(window, reverse);
}
}
IFACEMETHODIMP_(void)
WorkArea::ClearSelectedZones() noexcept
{

View file

@ -51,8 +51,9 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
*
* @param window Handle of window which should be assigned to zone.
* @param indexSet The set of zone indices within zone layout.
* @param suppressMove Whether we should just update the records or move window to the zone.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const ZoneIndexSet& indexSet) = 0;
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const ZoneIndexSet& indexSet, bool suppressMove = false) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow), based on zone index numbers,
* not their on-screen position.
@ -113,6 +114,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
* Update currently active zone layout for this work area.
*/
IFACEMETHOD_(void, UpdateActiveZoneSet)() = 0;
/**
* Cycle through tabs in the zone that the window is in.
*
* @param window Handle of window which is cycled from (the current tab).
* @param reverse Whether to cycle in reverse order (to the previous tab) or to move to the next tab.
*/
IFACEMETHOD_(void, CycleTabs)(HWND window, bool reverse) = 0;
/**
* Clear the selected zones when this WorkArea loses focus.
*/

View file

@ -130,7 +130,7 @@ public:
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, HWND workAreaWindow, ZoneIndex index) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet) noexcept;
MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet, bool suppressMove = false) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirectionAndIndex(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(bool)
@ -139,6 +139,10 @@ public:
ExtendWindowByDirectionAndPosition(HWND window, HWND workAreaWindow, DWORD vkCode) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByPoint(HWND window, HWND workAreaWindow, POINT ptClient) noexcept;
IFACEMETHODIMP_(void)
DismissWindow(HWND window) noexcept;
IFACEMETHODIMP_(void)
CycleTabs(HWND window, bool reverse) noexcept;
IFACEMETHODIMP_(bool)
CalculateZones(RECT workArea, int zoneCount, int spacing) noexcept;
IFACEMETHODIMP_(bool) IsZoneEmpty(ZoneIndex zoneIndex) const noexcept;
@ -151,6 +155,8 @@ private:
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept;
bool CalculateCustomLayout(Rect workArea, int spacing) noexcept;
bool CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing);
HWND GetNextTab(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;
void InsertTabIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
ZoneIndexSet ZoneSelectSubregion(const ZoneIndexSet& capturedZones, POINT pt) const;
ZoneIndexSet ZoneSelectClosestCenter(const ZoneIndexSet& capturedZones, POINT pt) const;
@ -160,6 +166,7 @@ private:
ZonesMap m_zones;
std::map<HWND, ZoneIndexSet> m_windowIndexSet;
std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets;
// Needed for ExtendWindowByDirectionAndPosition
std::map<HWND, ZoneIndexSet> m_windowInitialIndexSet;
@ -289,7 +296,7 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND workAreaWindow, ZoneIndex i
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& zoneIds) noexcept
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& zoneIds, bool suppressMove) noexcept
{
if (m_zones.empty())
{
@ -303,11 +310,13 @@ ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const Zo
m_windowInitialIndexSet.erase(window);
}
auto tabSortKeyWithinZone = GetTabSortKeyWithinZone(window);
DismissWindow(window);
RECT size;
bool sizeEmpty = true;
Bitmask bitmask = 0;
m_windowIndexSet[window] = {};
auto& indexSet = m_windowIndexSet[window];
for (ZoneIndex id : zoneIds)
{
@ -328,7 +337,7 @@ ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const Zo
sizeEmpty = false;
}
m_windowIndexSet[window].push_back(id);
indexSet.push_back(id);
}
if (id < std::numeric_limits<ZoneIndex>::digits)
@ -339,9 +348,14 @@ ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const Zo
if (!sizeEmpty)
{
SaveWindowSizeAndOrigin(window);
SizeWindowToRect(window, size);
if (!suppressMove)
{
SaveWindowSizeAndOrigin(window);
SizeWindowToRect(window, size);
}
StampWindow(window, bitmask);
InsertTabIntoZone(window, tabSortKeyWithinZone, indexSet);
}
}
@ -546,6 +560,110 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND workAreaWindow, POINT ptCli
MoveWindowIntoZoneByIndexSet(window, workAreaWindow, zones);
}
void ZoneSet::DismissWindow(HWND window) noexcept
{
auto& indexSet = m_windowIndexSet[window];
if (!indexSet.empty())
{
auto& windows = m_windowsByIndexSets[indexSet];
windows.erase(find(begin(windows), end(windows), window));
if (windows.empty())
{
m_windowsByIndexSets.erase(indexSet);
}
indexSet.clear();
}
SetTabSortKeyWithinZone(window, std::nullopt);
}
IFACEMETHODIMP_(void)
ZoneSet::CycleTabs(HWND window, bool reverse) noexcept
{
auto indexSet = GetZoneIndexSetFromWindow(window);
// Do nothing in case the window is not recognized
if (indexSet.empty())
{
return;
}
for (;;)
{
auto next = GetNextTab(indexSet, window, reverse);
if (next == NULL)
{
break;
}
auto success = SetForegroundWindow(next);
if (!success && GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
{
// Dismiss the encountered window since it was probably closed
DismissWindow(next);
continue;
}
break;
}
}
HWND ZoneSet::GetNextTab(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept
{
const auto& tabs = m_windowsByIndexSets[indexSet];
auto tabIt = std::find(tabs.begin(), tabs.end(), current);
if (!reverse)
{
++tabIt;
return tabIt == tabs.end() ? tabs.front() : *tabIt;
}
else
{
return tabIt == tabs.begin() ? tabs.back() : *(--tabIt);
}
}
void ZoneSet::InsertTabIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet)
{
if (tabSortKeyWithinZone.has_value())
{
// Insert the tab using the provided sort key
auto predicate = [tabSortKeyWithinZone](HWND tab) {
auto currentTabSortKeyWithinZone = GetTabSortKeyWithinZone(tab);
if (currentTabSortKeyWithinZone.has_value())
{
return currentTabSortKeyWithinZone.value() > tabSortKeyWithinZone;
}
else
{
return false;
}
};
auto position = std::find_if(m_windowsByIndexSets[indexSet].begin(), m_windowsByIndexSets[indexSet].end(), predicate);
m_windowsByIndexSets[indexSet].insert(position, window);
}
else
{
// Insert the tab at the end
tabSortKeyWithinZone = 0;
if (!m_windowsByIndexSets[indexSet].empty())
{
auto prevTab = m_windowsByIndexSets[indexSet].back();
auto prevTabSortKeyWithinZone = GetTabSortKeyWithinZone(prevTab);
if (prevTabSortKeyWithinZone.has_value())
{
tabSortKeyWithinZone = prevTabSortKeyWithinZone.value() + 1;
}
}
m_windowsByIndexSets[indexSet].push_back(window);
}
SetTabSortKeyWithinZone(window, tabSortKeyWithinZone);
}
IFACEMETHODIMP_(bool)
ZoneSet::CalculateZones(RECT workAreaRect, int zoneCount, int spacing) noexcept
{

View file

@ -64,8 +64,9 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
* current monitor desktop work area.
* @param indexSet The set of zone indices within zone layout.
* @param suppressMove Whether we should just update the records or move window to the zone.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet) = 0;
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet, bool suppressMove = false) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow), based on zone index numbers,
* not their on-screen position.
@ -119,6 +120,21 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByPoint)
(HWND window, HWND workAreaWindow, POINT ptClient) = 0;
/**
* Dismiss window from zone.
*
* @param window Handle of window which should be dismissed from zone.
*/
IFACEMETHOD_(void, DismissWindow)
(HWND window) = 0;
/**
* Cycle through tabs in the zone that the window is in.
*
* @param window Handle of window which is cycled from (the current tab).
* @param reverse Whether to cycle in reverse order (to the previous tab) or to move to the next tab.
*/
IFACEMETHOD_(void, CycleTabs)
(HWND window, bool reverse) = 0;
/**
* Calculate zone coordinates within zone layout based on number of zones and spacing.
*

View file

@ -52,7 +52,10 @@
#define ZoneBorderColorKey "ZoneBorderColor"
#define ZoneHighlightColorKey "ZoneHighlightColor"
#define ZoneHighlightOpacityKey "ZoneHighlightOpacity"
#define HotkeyKey "Hotkey"
#define EditorHotkeyKey "EditorHotkey"
#define WindowSwitchingToggleKey "WindowSwitchingToggle"
#define NextTabHotkey "NextTabHotkey"
#define PrevTabHotkey "PrevTabHotkey"
#define ExcludedAppsCountKey "ExcludedAppsCount"
#define KeyboardValueKey "KeyboardValue"
#define ActiveSetKey "ActiveSet"
@ -244,15 +247,21 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept
TraceLoggingBoolean(shortcutUsed, QuickLayoutSwitchedWithShortcutUsed));
}
static std::wstring HotKeyToString(const PowerToysSettings::HotkeyObject& hotkey)
{
return L"alt:" + std::to_wstring(hotkey.alt_pressed())
+ L", ctrl:" + std::to_wstring(hotkey.ctrl_pressed())
+ L", shift:" + std::to_wstring(hotkey.shift_pressed())
+ L", win:" + std::to_wstring(hotkey.win_pressed())
+ L", code:" + std::to_wstring(hotkey.get_code())
+ L", keyFromCode:" + hotkey.get_key();
}
void Trace::SettingsTelemetry(const Settings& settings) noexcept
{
const auto& editorHotkey = settings.editorHotkey;
std::wstring hotkeyStr = L"alt:" + std::to_wstring(editorHotkey.alt_pressed())
+ L", ctrl:" + std::to_wstring(editorHotkey.ctrl_pressed())
+ L", shift:" + std::to_wstring(editorHotkey.shift_pressed())
+ L", win:" + std::to_wstring(editorHotkey.win_pressed())
+ L", code:" + std::to_wstring(editorHotkey.get_code())
+ L", keyFromCode:" + editorHotkey.get_key();
auto editorHotkeyStr = HotKeyToString(settings.editorHotkey);
auto nextTabHotkeyStr = HotKeyToString(settings.nextTabHotkey);
auto prevTabHotkeyStr = HotKeyToString(settings.prevTabHotkey);
TraceLoggingWrite(
g_hProvider,
@ -281,7 +290,10 @@ void Trace::SettingsTelemetry(const Settings& settings) noexcept
TraceLoggingWideString(settings.zoneHighlightColor.c_str(), ZoneHighlightColorKey),
TraceLoggingInt32(settings.zoneHighlightOpacity, ZoneHighlightOpacityKey),
TraceLoggingInt32((int)settings.overlappingZonesAlgorithm, OverlappingZonesAlgorithmKey),
TraceLoggingWideString(hotkeyStr.c_str(), HotkeyKey),
TraceLoggingWideString(editorHotkeyStr.c_str(), EditorHotkeyKey),
TraceLoggingBoolean(settings.windowSwitching, WindowSwitchingToggleKey),
TraceLoggingWideString(nextTabHotkeyStr.c_str(), NextTabHotkey),
TraceLoggingWideString(prevTabHotkeyStr.c_str(), PrevTabHotkey),
TraceLoggingInt32(static_cast<int>(settings.excludedAppsArray.size()), ExcludedAppsCountKey));
}

View file

@ -70,6 +70,9 @@ namespace FancyZonesUnitTests
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
ptSettings.add_bool_toggle(L"fancyzones_windowSwitching", IDS_SETTING_WINDOW_SWITCHING_TOGGLE_LABEL, settings.windowSwitching);
ptSettings.add_hotkey(L"fancyzones_nextTab_hotkey", IDS_SETTING_NEXT_TAB_HOTKEY_LABEL, settings.nextTabHotkey);
ptSettings.add_hotkey(L"fancyzones_prevTab_hotkey", IDS_SETTING_PREV_TAB_HOTKEY_LABEL, settings.prevTabHotkey);
ptSettings.add_bool_toggle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
ptSettings.add_bool_toggle(L"fancyzones_mouseSwitch", IDS_SETTING_DESCRIPTION_MOUSESWITCH, settings.mouseSwitch);
ptSettings.add_bool_toggle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);

View file

@ -41,6 +41,7 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected.showZonesOnAllMonitors, actual.showZonesOnAllMonitors);
Assert::AreEqual(expected.spanZonesAcrossMonitors, actual.spanZonesAcrossMonitors);
Assert::AreEqual(expected.makeDraggedWindowTransparent, actual.makeDraggedWindowTransparent);
Assert::AreEqual(expected.windowSwitching, actual.windowSwitching);
Assert::AreEqual(expected.zoneColor.c_str(), actual.zoneColor.c_str());
Assert::AreEqual(expected.zoneBorderColor.c_str(), actual.zoneBorderColor.c_str());
Assert::AreEqual(expected.zoneHighlightColor.c_str(), actual.zoneHighlightColor.c_str());
@ -53,6 +54,8 @@ namespace FancyZonesUnitTests
}
compareHotkeyObjects(expected.editorHotkey, actual.editorHotkey);
compareHotkeyObjects(expected.nextTabHotkey, actual.nextTabHotkey);
compareHotkeyObjects(expected.prevTabHotkey, actual.prevTabHotkey);
}
TEST_CLASS (FancyZonesSettingsCreationUnitTest)
@ -62,7 +65,6 @@ namespace FancyZonesUnitTests
PCWSTR m_moduleKey = L"FancyZonesUnitTests";
std::wstring m_tmpName;
const PowerToysSettings::HotkeyObject m_defaultHotkeyObject = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_OEM_3);
const Settings m_defaultSettings;
TEST_METHOD_INITIALIZE(Init)
@ -128,6 +130,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
@ -168,6 +173,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
@ -202,6 +210,8 @@ namespace FancyZonesUnitTests
.zoneHighlightColor = L"#00FFD7",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3),
.nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_NEXT),
.prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_PRIOR),
.excludedApps = L"app",
.excludedAppsArray = { L"APP" },
};
@ -212,6 +222,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
@ -246,6 +259,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
@ -281,6 +297,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_zoneColor", expected.zoneColor);
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.add_property(L"fancyzones_excluded_apps", expected.excludedApps);
values.save_to_settings_file();
@ -354,6 +373,9 @@ namespace FancyZonesUnitTests
values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor);
values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity);
values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json());
values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching);
values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json());
values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json());
values.save_to_settings_file();
@ -416,6 +438,9 @@ namespace FancyZonesUnitTests
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION);
ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey);
ptSettings.add_bool_toggle(L"fancyzones_windowSwitching", IDS_SETTING_WINDOW_SWITCHING_TOGGLE_LABEL, settings.windowSwitching);
ptSettings.add_hotkey(L"fancyzones_nextTab_hotkey", IDS_SETTING_NEXT_TAB_HOTKEY_LABEL, settings.nextTabHotkey);
ptSettings.add_hotkey(L"fancyzones_prevTab_hotkey", IDS_SETTING_PREV_TAB_HOTKEY_LABEL, settings.prevTabHotkey);
ptSettings.add_bool_toggle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag);
ptSettings.add_bool_toggle(L"fancyzones_mouseSwitch", IDS_SETTING_DESCRIPTION_MOUSESWITCH, settings.mouseSwitch);
ptSettings.add_bool_toggle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys);
@ -517,6 +542,8 @@ namespace FancyZonesUnitTests
.zoneHighlightColor = L"#00AABB",
.zoneHighlightOpacity = 45,
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
.nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_NEXT),
.prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_PRIOR),
.excludedApps = L"app\r\napp2",
.excludedAppsArray = { L"APP", L"APP2" },
};

View file

@ -10,7 +10,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public class FZConfigProperties
{
// in reality, this file needs to be kept in sync currently with src\modules\fancyzones\lib\Settings.h
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, false, false, true, 0xc0);
public const int VkOem3 = 0xc0;
public const int VkNext = 0x22;
public const int VkPrior = 0x21;
public static readonly HotkeySettings DefaultEditorHotkeyValue = new HotkeySettings(true, false, false, true, VkOem3);
public static readonly HotkeySettings DefaultNextTabHotkeyValue = new HotkeySettings(true, false, false, false, VkNext);
public static readonly HotkeySettings DefaultPrevTabHotkeyValue = new HotkeySettings(true, false, false, false, VkPrior);
public FZConfigProperties()
{
@ -32,7 +38,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
FancyzonesSpanZonesAcrossMonitors = new BoolProperty();
FancyzonesZoneHighlightColor = new StringProperty(ConfigDefaults.DefaultFancyZonesZoneHighlightColor);
FancyzonesHighlightOpacity = new IntProperty(50);
FancyzonesEditorHotkey = new KeyboardKeysProperty(DefaultHotkeyValue);
FancyzonesEditorHotkey = new KeyboardKeysProperty(DefaultEditorHotkeyValue);
FancyzonesWindowSwitching = new BoolProperty(true);
FancyzonesNextTabHotkey = new KeyboardKeysProperty(DefaultNextTabHotkeyValue);
FancyzonesPrevTabHotkey = new KeyboardKeysProperty(DefaultPrevTabHotkeyValue);
FancyzonesMakeDraggedWindowTransparent = new BoolProperty();
FancyzonesExcludedApps = new StringProperty();
FancyzonesInActiveColor = new StringProperty(ConfigDefaults.DefaultFancyZonesInActiveColor);
@ -99,6 +108,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("fancyzones_editor_hotkey")]
public KeyboardKeysProperty FancyzonesEditorHotkey { get; set; }
[JsonPropertyName("fancyzones_windowSwitching")]
public BoolProperty FancyzonesWindowSwitching { get; set; }
[JsonPropertyName("fancyzones_nextTab_hotkey")]
public KeyboardKeysProperty FancyzonesNextTabHotkey { get; set; }
[JsonPropertyName("fancyzones_prevTab_hotkey")]
public KeyboardKeysProperty FancyzonesPrevTabHotkey { get; set; }
[JsonPropertyName("fancyzones_excluded_apps")]
public StringProperty FancyzonesExcludedApps { get; set; }

View file

@ -89,6 +89,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
_highlightOpacity = Settings.Properties.FancyzonesHighlightOpacity.Value;
_excludedApps = Settings.Properties.FancyzonesExcludedApps.Value;
EditorHotkey = Settings.Properties.FancyzonesEditorHotkey.Value;
_windowSwitching = Settings.Properties.FancyzonesWindowSwitching.Value;
NextTabHotkey = Settings.Properties.FancyzonesNextTabHotkey.Value;
PrevTabHotkey = Settings.Properties.FancyzonesPrevTabHotkey.Value;
// set the callback functions value to hangle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
@ -127,6 +130,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private int _highlightOpacity;
private string _excludedApps;
private HotkeySettings _editorHotkey;
private bool _windowSwitching;
private HotkeySettings _nextTabHotkey;
private HotkeySettings _prevTabHotkey;
private string _zoneInActiveColor;
private string _zoneBorderColor;
private string _zoneHighlightColor;
@ -152,6 +158,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
OnPropertyChanged(nameof(IsEnabled));
OnPropertyChanged(nameof(SnapHotkeysCategoryEnabled));
OnPropertyChanged(nameof(QuickSwitchEnabled));
OnPropertyChanged(nameof(WindowSwitchingCategoryEnabled));
}
}
}
@ -172,6 +179,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
public bool WindowSwitchingCategoryEnabled
{
get
{
return _isEnabled && _windowSwitching;
}
}
public bool ShiftDrag
{
get
@ -604,7 +619,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
if (value == null || value.IsEmpty())
{
_editorHotkey = FZConfigProperties.DefaultHotkeyValue;
_editorHotkey = FZConfigProperties.DefaultEditorHotkeyValue;
}
else
{
@ -617,6 +632,78 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
public bool WindowSwitching
{
get
{
return _windowSwitching;
}
set
{
if (value != _windowSwitching)
{
_windowSwitching = value;
Settings.Properties.FancyzonesWindowSwitching.Value = _windowSwitching;
NotifyPropertyChanged();
OnPropertyChanged(nameof(WindowSwitchingCategoryEnabled));
}
}
}
public HotkeySettings NextTabHotkey
{
get
{
return _nextTabHotkey;
}
set
{
if (value != _nextTabHotkey)
{
if (value == null || value.IsEmpty())
{
_nextTabHotkey = FZConfigProperties.DefaultNextTabHotkeyValue;
}
else
{
_nextTabHotkey = value;
}
Settings.Properties.FancyzonesNextTabHotkey.Value = _nextTabHotkey;
NotifyPropertyChanged();
}
}
}
public HotkeySettings PrevTabHotkey
{
get
{
return _prevTabHotkey;
}
set
{
if (value != _prevTabHotkey)
{
if (value == null || value.IsEmpty())
{
_prevTabHotkey = FZConfigProperties.DefaultPrevTabHotkeyValue;
}
else
{
_prevTabHotkey = value;
}
Settings.Properties.FancyzonesPrevTabHotkey.Value = _prevTabHotkey;
NotifyPropertyChanged();
}
}
}
public string ExcludedApps
{
get

View file

@ -51,6 +51,9 @@ namespace ViewModelTests
Assert.AreEqual(originalSettings.Properties.FancyzonesBorderColor.Value, viewModel.ZoneBorderColor);
Assert.AreEqual(originalSettings.Properties.FancyzonesDisplayChangeMoveWindows.Value, viewModel.DisplayChangeMoveWindows);
Assert.AreEqual(originalSettings.Properties.FancyzonesEditorHotkey.Value.ToString(), viewModel.EditorHotkey.ToString());
Assert.AreEqual(originalSettings.Properties.FancyzonesWindowSwitching.Value, viewModel.WindowSwitching);
Assert.AreEqual(originalSettings.Properties.FancyzonesNextTabHotkey.Value.ToString(), viewModel.NextTabHotkey.ToString());
Assert.AreEqual(originalSettings.Properties.FancyzonesPrevTabHotkey.Value.ToString(), viewModel.PrevTabHotkey.ToString());
Assert.AreEqual(originalSettings.Properties.FancyzonesExcludedApps.Value, viewModel.ExcludedApps);
Assert.AreEqual(originalSettings.Properties.FancyzonesHighlightOpacity.Value, viewModel.HighlightOpacity);
Assert.AreEqual(originalSettings.Properties.FancyzonesInActiveColor.Value, viewModel.ZoneInActiveColor);

View file

@ -449,6 +449,24 @@
<data name="FancyZones_HotkeyEditorControl.Header" xml:space="preserve">
<value>Open layout editor</value>
<comment>Shortcut to launch the FancyZones layout editor application</comment>
</data>
<data name="FancyZones_WindowSwitching_GroupSettings.Header" xml:space="preserve">
<value>Window switching</value>
</data>
<data name="FancyZones_WindowSwitching_GroupSettings.Description" xml:space="preserve">
<value>Shortcuts for switching between windows in the current zone</value>
</data>
<data name="FancyZones_HotkeyNextTabControl.Header" xml:space="preserve">
<value>Next window</value>
</data>
<data name="FancyZones_HotkeyNextTabControl.Description" xml:space="preserve">
<value>Shortcut for switching to the next window in the current zone</value>
</data>
<data name="FancyZones_HotkeyPrevTabControl.Header" xml:space="preserve">
<value>Previous window</value>
</data>
<data name="FancyZones_HotkeyPrevTabControl.Description" xml:space="preserve">
<value>Shortcut for switching to the previous window in the current zone</value>
</data>
<data name="SettingsPage_SetShortcut.AutomationProperties.Name" xml:space="preserve">
<value>Shortcut setting</value>

View file

@ -148,6 +148,31 @@
</controls:SettingExpander.Content>
</controls:SettingExpander>
<controls:SettingExpander IsExpanded="True">
<controls:SettingExpander.Header>
<controls:Setting x:Uid="FancyZones_WindowSwitching_GroupSettings" Icon="&#9112;" Style="{StaticResource ExpanderHeaderSettingStyle}">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.WindowSwitching}"/>
</controls:Setting.ActionContent>
</controls:Setting>
</controls:SettingExpander.Header>
<controls:SettingExpander.Content>
<StackPanel>
<controls:Setting x:Uid="FancyZones_HotkeyNextTabControl" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.WindowSwitchingCategoryEnabled}" Icon="&#9112;" Style="{StaticResource ExpanderContentSettingStyle}">
<controls:Setting.ActionContent>
<controls:ShortcutControl HotkeySettings="{x:Bind Path=ViewModel.NextTabHotkey, Mode=TwoWay}" MinWidth="{StaticResource SettingActionControlMinWidth}"/>
</controls:Setting.ActionContent>
</controls:Setting>
<Rectangle Style="{StaticResource ExpanderSeparatorStyle}" />
<controls:Setting x:Uid="FancyZones_HotkeyPrevTabControl" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.WindowSwitchingCategoryEnabled}" Icon="&#9111;" Style="{StaticResource ExpanderContentSettingStyle}">
<controls:Setting.ActionContent>
<controls:ShortcutControl HotkeySettings="{x:Bind Path=ViewModel.PrevTabHotkey, Mode=TwoWay}" MinWidth="{StaticResource SettingActionControlMinWidth}"/>
</controls:Setting.ActionContent>
</controls:Setting>
</StackPanel>
</controls:SettingExpander.Content>
</controls:SettingExpander>
<controls:SettingExpander IsExpanded="True">
<controls:SettingExpander.Header>
<controls:Setting x:Uid="FancyZones_OverrideSnapHotkeys" Icon="&#xE145;" Style="{StaticResource ExpanderHeaderSettingStyle}">