-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Fancy Zones deep dive
FancyZones is the base class that runs the show. It uses hooks to monitor for windows entering and exiting the move/size loop and to listen for key presses for hotkey interception. For every connected display, it creates a ZoneWindow which is used to display the active ZoneSet per monitor for use when editing the layout or displaying the drop targets. A ZoneSet is composed of one or more Zones which are the locations where windows can be easily positioned.
The main driving force behind FancyZones is the accessibility hook used to know when a window enters the move/size loop. It listens for EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, and EVENT_OBJECT_LOCATIONCHANGE. For each of these three events, it forwards on to the ZoneWindow associated with the monitor that the window being dragged is currently on.
A low-level keyboard hook is installed in order to, optionally, intercept Window+Arrow hotkeys. Traditionally, Win+Left/Right arrow will move a window between Windows Snap regions. This hook allows FancyZones to use Win+Left/Right arrow to move windows between Zones. The hook also allows using 0-9 to change the active ZoneSet during a drag operation.
During initial standup, FancyZones creates a ZoneWindow for each connected monitor. When it receives a WM_DISPLAYCHANGE, it updates the available ZoneWindows to reflect the state of the system (eg add a new ZoneWindow for newly connected monitor, delete ZoneWindow for disconnected monitor, etc)
interface IFancyZones : public IUnknown
{
// Returns the main application window
IFACEMETHOD_(HWND, GetWindow)() = 0;
// Returns the global HINSTANCE for the process
IFACEMETHOD_(HINSTANCE, GetHInstance)() = 0;
// Returns the global Settings object used to look up individual settings throughout the product
IFACEMETHOD_(Settings, GetSettings)() = 0;
// Used in WinMain to initialize FancyZones and enter the message loop
IFACEMETHOD_(void, Run)() = 0;
// Toggles the visibility of all ZoneWindows
IFACEMETHOD_(void, ToggleZoneViewers)() = 0;
// Shows a single ZoneWindow in editor mode on the provided monitor
IFACEMETHOD_(void, ShowZoneEditorForMonitor)(_In_ HMONITOR monitor) = 0;
// Returns true if we are currently detecting a movesize loop
IFACEMETHOD_(bool, InMoveSize)() = 0;
// Called by the event hook in response to EVENT_SYSTEM_MOVESIZESTART
IFACEMETHOD(MoveSizeEnter)(_In_ HWND window, _In_ HMONITOR monitor, POINT ptScreen) = 0;
// Called by the event hook in response to EVENT_SYSTEM_MOVESIZEEND
IFACEMETHOD(MoveSizeExit)(_In_ HWND window, POINT ptScreen) = 0;
// Called by the event hook in response to EVENT_OBJECT_LOCATIONCHANGE
IFACEMETHOD(MoveSizeUpdate)(_In_ HMONITOR monitor, POINT ptScreen) = 0;
// Called during startup or on WM_DISPLAYCHANGE to add a ZoneWindow to the collection
// There will be one ZoneWindow per connected monitor
IFACEMETHOD(AddZoneWindow)(_In_ IZoneWindow* zoneWindow, _In_ HMONITOR monitor) = 0;
// Called in response to WM_DISPLAYCHANGE from the main application window
IFACEMETHOD_(void, OnDisplayChange)(DisplayChangeType changeType) = 0;
// Used to move the specified HWND into Zone
// The ZoneSet used is found by looking up the monitor that window is currently on
// This gets called to keep windows in their current zones after a WM_DISPLAYCHANGE
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(_In_ HWND window, int index) = 0;
// Used to filter out windows that the hook should not be processing
// Currently checks if the window's GWL_STYLE has WS_MAXIMIZEBOX
IFACEMETHOD_(bool, IsInterestingWindow)(_In_ HWND window) = 0;
// Called byt the event hook in response to EVENT_OBJECT_NAMECHANGE on the Desktop window
// The accessible name of the desktop window changes when the current virtual desktop changes
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
// Returns the GUID of the current active virtual desktop
IFACEMETHOD_(GUID, GetCurrentVirtualDesktopId)() = 0;
// Called by the LL keyboard hook
// Used to override snap hotkeys and to change the active ZoneSet during a drag
IFACEMETHOD_(bool, OnKeyDown)(LPARAM lparam) = 0;
// Keep windows positioned inside their zones when the active ZoneSet changes
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
};
ZoneWindow is used to display the Zones a user can drop a window in during a drag operation, flash the Zones when the ZoneSet changes, and draw the Zone Editor UI when in edit mode. Basically, when a ZoneSet needs to be visualized, ZoneWindow does it.
interface IZoneWindow : public IUnknown
{
// Shows the ZoneWindow
// If activate is true, set foreground to the window otherwise just show
IFACEMETHOD(ShowZoneWindow)(bool activate) = 0;
// Hide the ZoneWindow
IFACEMETHOD(HideZoneWindow)() = 0;
// Called when the drag enters the monitor this ZoneWindow is assigned to
IFACEMETHOD(MoveSizeEnter)(_In_ HWND window, POINT ptScreen, DragMode dragMode) = 0;
// Called when the drag exits the monitor
IFACEMETHOD(MoveSizeExit)(_In_ HWND window, POINT ptScreen) = 0;
// Called when the drag updates position on this monitor
IFACEMETHOD(MoveSizeUpdate)(POINT ptScreen, DragMode dragMode) = 0;
// Called when a drag ends and the window is not dropped in a Zone
IFACEMETHOD(MoveSizeCancel)() = 0;
// Returns the DragMode of the current drag operation
// DragMode allows for overriding drag behavior via settings or via hotkey
IFACEMETHOD_(DragMode, GetDragMode)() = 0;
// Part of the chain to move a window into a specific Zone
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(_In_ HWND window, int index) = 0;
// Used to cycle a window between zones via the hijacked snap hotkeys
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(_In_ HWND window, DWORD vkCode) = 0;
// Called in response to WM_DISPLAYCHANGE
// Allows cleanup, if necessary, since ZoneWindow will be destroyed shortly thereafter
IFACEMETHOD_(void, OnDisplayChange)(DisplayChangeType type) = 0;
// Allows changing the active ZoneSet via key press either during a drag or while the ZoneWindow is in foreground
IFACEMETHOD_(void, CycleActiveZoneSet)(DWORD vkCode) = 0;
};
Collection of one or more Zones. Only one ZoneSet is active at a time per monitor.
interface IZoneSet : public IUnknown
{
// Gets the unique ID used to identify this ZoneSet
IFACEMETHOD_(GUID, GetId)() = 0;
// Adds a Zone to the collection
IFACEMETHOD(AddZone)(_In_ Microsoft::WRL::ComPtr<IZone> zone, bool front) = 0;
// Removes a Zone from the collection
IFACEMETHOD(RemoveZone)(_In_ Microsoft::WRL::ComPtr<IZone> zone) = 0;
// Returns the topmost Zone at the given point
IFACEMETHOD_(Microsoft::WRL::ComPtr<IZone>, ZoneFromPoint)(POINT pt) = 0;
// Returns a Zone that the window is in
// Will return nullptr if the window is not in a Zone
IFACEMETHOD_(Microsoft::WRL::ComPtr<IZone>, ZoneFromWindow)(_In_ HWND window) = 0;
// Gets all the Zones
IFACEMETHOD_(std::vector<Microsoft::WRL::ComPtr<IZone>>, GetZones)() = 0;
// ZoneSetLayout
// * Grid - Pregenerated layout (2x2, 3x3, etc)
// * Row - Pregenerated layout in a single row
// * Focus - Pregenerated layout with a central focus Zone and fanned peripheral Zones
// * Custom - User generated Zone
IFACEMETHOD_(ZoneSetLayout, GetLayout)() = 0;
// The amount of default padding between Zones in a generated layout
IFACEMETHOD_(int, GetInnerPadding)() = 0;
// Makes a copy of the IZoneSet and marks it as ZoneSetLayout::Custom
IFACEMETHOD_(Microsoft::WRL::ComPtr<IZoneSet>, MakeCustomClone)() = 0;
// Persists ZoneSet data to the registry
IFACEMETHOD_(void, Save)() = 0;
// Moves a Zone to the front of the collection
IFACEMETHOD_(void, MoveZoneToFront)(_In_ Microsoft::WRL::ComPtr<IZone> zone) = 0;
// Moves a Zone to the back of the collection
IFACEMETHOD_(void, MoveZoneToBack)(_In_ Microsoft::WRL::ComPtr<IZone> zone) = 0;
// Part of the chain to move a window into a specific Zone
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(_In_ HWND window, _In_ HWND zoneWindow, int index) = 0;
// Part of the chain to move a window into a specific Zone
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(_In_ HWND window, _In_ HWND zoneWindow, DWORD vkCode) = 0;
// Called when a drag ends or leaves the monitor this ZoneWindow is on
// This will remove the window from its currently assigned Zone and assign it
// to a different Zone based on the current cursor position
IFACEMETHOD_(void, MoveSizeExit)(_In_ HWND window, _In_ HWND zoneWindow, _In_ POINT ptClient) = 0;
};
Basically a RECT and a map of HWND->RECT to keep track of where windows can be placed and which windows are currently in the Zone.
interface IZone : public IUnknown
{
// Returns the RECT that this Zone represents
IFACEMETHOD_(RECT, GetZoneRect)() = 0;
// Returns true if the specified window is in this Zone's collection
IFACEMETHOD_(bool, ContainsWindow)(_In_ HWND window) = 0;
// Adds the window the collection
IFACEMETHOD_(void, AddWindowToZone)(_In_ HWND window, _In_ HWND zoneWindow, bool stampZone) = 0;
// Removes the window from the collection
IFACEMETHOD_(void, RemoveWindowFromZone)(_In_ HWND window, bool restoreSize) = 0;
// Sets an id for this Zone
// The id will be unique per ZoneSet
IFACEMETHOD_(void, SetId)(size_t id) = 0;
// Returns the id given to this Zone
IFACEMETHOD_(size_t, GetId)() = 0;
};