# Implementing FancyZones Window Layout Zones: PowerToys Development Guide

> Learn to implement FancyZones window layout zones with this PowerToys development guide. Discover the three-layer architecture, JSON persistence, and programmatic window snapping.

- Repository: [Microsoft/PowerToys](https://github.com/microsoft/PowerToys)
- Tags: development-guide
- Published: 2026-02-25

---

**FancyZones splits monitors into configurable zones using a three-layer architecture where WorkArea objects manage zone snapping, layouts persist as JSON files, and hot-key events route through [`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp) to snap windows programmatically.**

FancyZones is the window management module in Microsoft PowerToys that enables users to organize desktop space through customizable snap zones. When implementing FancyZones window layout zones, developers interact with a C++ core engine, a WPF editor, and JSON-based persistence layers. This guide examines the source code architecture in `microsoft/PowerToys` to explain how zones are created, applied, and extended.

## Architecture Overview

The FancyZones implementation follows a strict three-layer separation to isolate window management logic from UI and persistence concerns.

**Layer 1: Module Entry Point** – [`src/modules/fancyzones/FancyZones/FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZones/FancyZones.cpp) implements the `IFancyZones` interface and hosts the main window-message loop. This layer receives global hook events, hot-key input, and orchestrates updates across all monitors.

**Layer 2: Work-Area Model** – [`src/modules/fancyzones/FancyZonesLib/WorkArea.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesLib/WorkArea.h) and [`WorkArea.cpp`](https://github.com/microsoft/PowerToys/blob/main/WorkArea.cpp) represent a unique *monitor + virtual-desktop* pair. Each `WorkArea` owns a `ZoneSet` and exposes methods to create, snap, cycle, and update windows for that specific display context.

**Layer 3: Layout Data** – [`FancyZonesDataTypes.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesDataTypes.h), [`FancyZonesData.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesData.h), and [`CustomLayouts.h`](https://github.com/microsoft/PowerToys/blob/main/CustomLayouts.h) define the serializable structures (`Zone`, `ZoneSet`, `CustomLayoutData`) that describe grid, canvas, and custom layouts. The module communicates with the FancyZones editor (a separate WPF app) via JSON files read/written by [`FancyZonesEditorIO.cs`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesEditorIO.cs).

## Work-Area Lifecycle

Understanding the lifecycle of a **WorkArea** is essential for implementing FancyZones window layout zones correctly.

First, the system detects monitors and virtual desktops using `MonitorUtils::IdentifyMonitors()` and `VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry()`. For each unique pair, `WorkArea::Create` instantiates a new work-area via `AddWorkArea` in [`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp).

Next, the engine assigns a **ZoneSet** loaded from [`AppliedLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/AppliedLayouts.json) (or defaults) using `AppliedLayouts::instance().ApplyLayout`. When users trigger a hot-key or a new window appears, `WorkArea::Snap` calculates the target position and resizes the window accordingly.

Finally, whenever a display change, virtual-desktop switch, or layout reload occurs, `FancyZones::OnDisplayChange` evaluates `ShouldWorkAreasBeRecreated` to determine whether existing objects can be reused or must be rebuilt from scratch.

## Layout Types and Data Models

FancyZones supports four distinct layout types defined in [`FancyZonesDataTypes.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesDataTypes.h).

**Grid layouts** use `GridLayoutInfo` to define uniform rows and columns with optional per-cell sizing percentages. **Canvas layouts** store arbitrary rectangles via `CanvasLayoutInfo`, allowing free-form zone placement. **PriorityGrid** extends the grid concept with directional ordering for cursor-based zone selection. **Custom layouts** wrap any of the above under a UUID stored in `CustomLayoutData`.

All layout data serialize to JSON using the helper methods in [`FancyZonesEditorCommon/Data/FancyZonesJsonContext.cs`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesEditorCommon/Data/FancyZonesJsonContext.cs). The editor writes to [`AppliedLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/AppliedLayouts.json), [`CustomLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/CustomLayouts.json), and [`LayoutHotkeys.json`](https://github.com/microsoft/PowerToys/blob/main/LayoutHotkeys.json), while the main module watches these files via `FileWatcher` ([`common/SettingsAPI/FileWatcher.h`](https://github.com/microsoft/PowerToys/blob/main/common/SettingsAPI/FileWatcher.h)) to reload configurations dynamically.

## Hot-Key Processing Pipeline

The low-level keyboard hook resides in [`KeyboardInput.cpp`](https://github.com/microsoft/PowerToys/blob/main/KeyboardInput.cpp). When a snap hot-key is pressed, `FancyZones::ShouldProcessSnapHotkey` validates the foreground window and current layout before delegating to `WindowKeyboardSnap` or `WindowMouseSnap`.

`WindowKeyboardSnap` calculates the target `ZoneIndexSet` based on arrow-key direction and invokes `WorkArea::Snap` to execute the move. This pipeline ensures that window positioning logic remains decoupled from raw input handling, allowing developers to inject custom snapping behaviors at the `WorkArea` layer.

## Inter-Process Communication

The FancyZones module launches the editor using `ShellExecuteEx` (see `ToggleEditor` in [`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp)). Before launch, `EditorParameters::Save` writes monitor rectangles and the "span-across-monitors" flag to [`editor-parameters.json`](https://github.com/microsoft/PowerToys/blob/main/editor-parameters.json).

Both processes remain synchronized through shared JSON files monitored by `FileWatcher`. When the editor closes, the main module receives a `WM_PRIV_EDITOR` message, reloads the configuration, and rebuilds its `WorkArea` objects to reflect any layout changes made by the user.

## Extending FancyZones: Step-by-Step Implementation

When adding a new zone type or custom behavior to FancyZones, follow this precise development flow:

1. **Define the data model** – Extend [`FancyZonesDataTypes.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesDataTypes.h) with a new struct (e.g., `RadialLayoutInfo`).
2. **Add JSON serialization** – Update [`FancyZonesJsonContext.cs`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesJsonContext.cs) to read and write the new fields.
3. **Create a layout class** – Derive from `Layout` (found in [`FancyZonesLib/WorkArea.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesLib/WorkArea.h)) and implement `CalculateZones` and `Snap`.
4. **Update the layout registry** – Modify `CustomLayouts::GetLayout` to recognize the new `CustomLayoutData::type`.
5. **Expose UI controls** – Add XAML elements in `FancyZonesEditor.xaml` and bind them via `MainWindowSettingsModel`.

## Practical Implementation Examples

### Programmatically Add a Custom Grid Layout

The following C# example demonstrates creating a 2x3 grid layout and persisting it for the primary monitor:

```csharp
using Microsoft.PowerToys.FancyZonesEditor.Common;
using FancyZonesEditor.Models;

// Build a GridLayoutInfo (rows = 2, columns = 3)
var grid = new GridLayoutModel("MyCustomGrid", LayoutType.Custom)
{
    Rows = 2,
    Columns = 3,
    RowPercents = new List<int> { 50, 50 },
    ColumnPercents = new List<int> { 33, 34, 33 },
    Zones = new List<Rectangle>()
};

// Convert to JSON-serializable structure
var layoutData = new CustomLayoutData
{
    name = grid.Name,
    type = CustomLayoutType.Grid,
    info = grid.ToGridLayoutInfo()
};

// Persist to CustomLayouts.json
FancyZonesEditorIO.SerializeCustomLayout(layoutData);

```

*Key files:* [`FancyZonesEditor.Common/FancyZonesDataIO.cs`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesEditor.Common/FancyZonesDataIO.cs), [`FancyZonesEditor.Models/GridLayoutModel.cs`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesEditor.Models/GridLayoutModel.cs)

### Apply a Layout from Native C++ Code

To apply a layout programmatically after a display change:

```cpp
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/FancyZonesLib.h>

void ApplyMyLayout(const FancyZonesDataTypes::WorkAreaId& waId)
{
    // Retrieve layout by UUID
    auto layoutOpt = CustomLayouts::instance().GetLayout(myLayoutId);
    if (!layoutOpt) return;

    // Apply to work-area
    if (AppliedLayouts::instance().ApplyLayout(waId, layoutOpt.value()))
    {
        // Refresh visual zones
        FancyZones::RefreshLayouts();
    }
}

```

*Key files:* [`FancyZonesLib/CustomLayouts.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesLib/CustomLayouts.h), [`FancyZonesLib/AppliedLayouts.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesLib/AppliedLayouts.h), [`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp)

### Hook a New Hot-Key for Multi-Monitor Spanning

Register and handle a custom hot-key in the main module:

```cpp
// Registration in FancyZones::Run()
constexpr int HOTKEY_SPAN = 4;
auto hk = FancyZonesSettings::settings().spanZonesAcrossMonitorsHotkey;
RegisterHotKey(m_window, HOTKEY_SPAN, hk.get_modifiers(), hk.get_code());

// Handling in WndProc
case WM_HOTKEY:
    if (wparam == HOTKEY_SPAN)
    {
        FancyZonesSettings::instance().UpdateSetting(
            SettingId::SpanZonesAcrossMonitors,
            !FancyZonesSettings::settings().spanZonesAcrossMonitors);
    }
    break;

```

*Key files:* [`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp), [`FancyZonesSettings.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesSettings.h)

## Key Source Files

| Path | Role |
|------|------|
| [`src/modules/fancyzones/FancyZones/FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZones/FancyZones.cpp) | Core module implementation, hook handling, and work-area orchestration. |
| [`src/modules/fancyzones/FancyZonesLib/WorkArea.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesLib/WorkArea.h) / [`WorkArea.cpp`](https://github.com/microsoft/PowerToys/blob/main/WorkArea.cpp) | Per-monitor/desktop zone management, snap logic, and cycling. |
| [`src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h) | Data models for zones, layouts, and virtual-desktop IDs. |
| [`src/modules/fancyzones/FancyZonesLib/CustomLayouts.h`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesLib/CustomLayouts.h) | Registry for user-defined layouts (load, query, apply). |
| [`src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs) | WPF editor UI and JSON persistence logic. |
| [`src/modules/fancyzones/editor/FancyZonesEditor/Common/FancyZonesDataIO.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/editor/FancyZonesEditor/Common/FancyZonesDataIO.cs) | Serialization helpers for layout data. |
| [`src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs`](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs) | Settings UI glue for hot-keys and toggles. |

## Summary

- **FancyZones** uses a three-tier architecture separating module logic ([`FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/FancyZones.cpp)), per-display state (`WorkArea`), and data models ([`FancyZonesDataTypes.h`](https://github.com/microsoft/PowerToys/blob/main/FancyZonesDataTypes.h)).
- **WorkArea** objects manage the lifecycle of zones for each monitor/virtual-desktop pair, reloading when `OnDisplayChange` detects configuration changes.
- **JSON files** ([`AppliedLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/AppliedLayouts.json), [`CustomLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/CustomLayouts.json)) serve as the communication bridge between the C++ engine and the WPF editor, watched by `FileWatcher`.
- **Hot-key processing** flows from [`KeyboardInput.cpp`](https://github.com/microsoft/PowerToys/blob/main/KeyboardInput.cpp) through validation routines to `WorkArea::Snap` for execution.
- **Extensions** require updating the data model, JSON context, layout class, and editor UI to maintain consistency across the codebase.

## Frequently Asked Questions

### How does FancyZones detect monitor layout changes?

FancyZones monitors system events through `MonitorUtils::IdentifyMonitors()` and `VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry()`. When a change occurs, `FancyZones::OnDisplayChange` evaluates `ShouldWorkAreasBeRecreated` to decide whether to reuse existing `WorkArea` objects or reconstruct them with updated zone configurations.

### What is the difference between Grid and Canvas layouts?

**Grid layouts** (`GridLayoutInfo`) divide screens into uniform rows and columns with percentage-based sizing, ideal for structured workflows. **Canvas layouts** (`CanvasLayoutInfo`) store arbitrary pixel rectangles, allowing free-form zone placement anywhere on the display. Both types serialize to JSON and instantiate `ZoneSet` objects through `CustomLayouts::GetLayout`.

### Can I trigger zone snapping programmatically without user input?

Yes. Native code can invoke `WorkArea::Snap` directly after retrieving a layout via `CustomLayouts::instance().GetLayout` and applying it through `AppliedLayouts::instance().ApplyLayout`. For C# scenarios, use `FancyZonesEditorIO.SerializeCustomLayout` to persist layouts that the engine will load automatically.

### Where are custom layouts stored on disk?

Custom layouts persist in `%LocalAppData%\Microsoft\PowerToys\FancyZones\CustomLayouts.json`, while active monitor assignments reside in [`AppliedLayouts.json`](https://github.com/microsoft/PowerToys/blob/main/AppliedLayouts.json). Hot-key mappings use [`LayoutHotkeys.json`](https://github.com/microsoft/PowerToys/blob/main/LayoutHotkeys.json). Both the C++ module and the WPF editor watch these files via `FileWatcher` to synchronize state changes immediately.