Issue 1:
ℹ Computer information

PowerToys version: 0.23.0

PowerToy Utility: FancyZones

Running PowerToys as Admin: No

Windows build number: 1909 (18363.1082)

📝 Provide detailed reproduction steps (if any)

Open the FZ editor with Win+`

Set the number of zones to 7

Select the Priority Grid layout

Select "Edit selected layout"

Drag the slider between zone 3 and zone 5 up and down repeatedly.

✔️ Expected result

Zones 3/5 resize.

❌ Actual result

What is the actual result of the above steps?

Zone 4,5 continually increases in size, and zones 1,6,7 decrease to accomodate. See screenshot

📷 Screenshots

Are there any useful screenshots? WinKey+Shift+S and then just paste them directly into the form

Issue 2:
Summary of the new feature/enhancement

Improve current implementation of the FancyZones Editor that is complicated and hard to maintain.

Proposal created in cooperation with @ivan100sic.

Proposed technical implementation details (optional)

Data structures:

Zone: {
int index;
double width;
double height;
double x;
double y;
}

Splitter: {
Orientation orientation; //Horizontal or Vertical
double position;
List<int> negativeSideIndexes; //left/top zone indexes
List<int> positiveSideIndexes; //right/bottom zone indexes
}

Use cases

Drag splitter:

Move left: reduce width/height for each grid zone that has index contained in negativeSideIndexes and increase those parameters for positiveSideIndexes.

Move right: similarly

Move is possible until every zone width/height is bigger than some small constant, ideally so that zone index numbers are not clipped.

Split zone:

Non-snapped, snapped non-adjacent: update indexes in zones and in splitters

Rule for updating splitters:

index > splittedIndex -> index++
index == splittedIndex && orientation != newSplitterOrientation -> insert(index + 1)
index == splittedIndex && orientation == newSplitterOrientation && list == negativeSideIndexes -> index++

Examples:

splittedIndex == 0
newSplitterOrientation == Horizontal

Splitters update:
{ orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 3 ] } -> { orientation: Horizontal, negativeSideIndexes: [ 1, 2 ], positiveSideIndexes: [ 3, 4 ] }
{ orientation: Vertical, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 1, 3 ] } -> { orientation: Vertical, negativeSideIndexes: [ 0, 1, 3 ], positiveSideIndexes: [ 2, 4 ] }

splittedIndex == 1
newSplitterOrientation == Horizontal

Splitters update:
{ orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 3 ] } -> { orientation: Horizontal, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 3, 4 ] }
{ orientation: Vertical, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 1, 3 ] } -> { orientation: Vertical, negativeSideIndexes: [ 0, 3 ], positiveSideIndexes: [ 1, 2, 4 ] }

splittedIndex == 2
newSplitterOrientation == Horizontal

Splitters update:
{ orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 3 ] } -> { orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 4 ] }
{ orientation: Vertical, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 1, 3 ] } -> { orientation: Vertical, negativeSideIndexes: [ 0, 2, 3 ], positiveSideIndexes: [ 1, 4 ] }

splittedIndex == 3
newSplitterOrientation == Horizontal

Splitters update:
{ orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 3 ] } -> { orientation: Horizontal, negativeSideIndexes: [ 0, 1 ], positiveSideIndexes: [ 2, 3 ] }
{ orientation: Vertical, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 1, 3 ] } -> { orientation: Vertical, negativeSideIndexes: [ 0, 2 ], positiveSideIndexes: [ 1, 3, 4 ] }

Snapped adjacent:

Update existing splitter if

orientation same

position same

one of the zones from negativeSideIndexes has bottom/right side == splitted zone top/left

one of the zones from positiveSideIndexes has top/left side == splitted zone bottom/right

Otherwise add new splitter

Don't to show splitter if it splits the zone into two pieces which are too small (less than Minimum Bounding Rectangle).

Merge zones

Remove merged zones and add a new zone corresponding to Minimum Bounding Rectangle (use the closure algorithm ExtendRangeToHaveEvenCellEdges that should be reimplemented to work with a list of rectangles instead of cell child map).

Update zone indexes

Update splitters

Replace merged zones indexes with first merged zone index

Update other indexes

if negativeSideIndexes and positiveSideIndexes both contain same index after previous steps, remove that index from both lists

if negativeSideIndexes or positiveSideIndexes contain only merged indexes, remove splitter

Example:

merged: [ 2, 3, 7, 8, 9 ]

Splitters
{ Horizontal, negativeSideIndexes: [ 3 ], positiveSideIndexes: [ 8, 9 ] } -> removed
{ Horizontal, negativeSideIndexes: [ 1, 2 ], positiveSideIndexes: [ 5, 6, 7 ] } -> { Horizontal, negativeSideIndexes: [ 1 ], positiveSideIndexes: [ 5, 6 ] }
{ Horizontal, negativeSideIndexes: [ 7, 8, 9 ], positiveSideIndexes: [ 10, 11 ] } -> { Horizontal, negativeSideIndexes: [ 2 ], positiveSideIndexes: [ 10, 11 ] }
{ Vertical, negativeSideIndexes: [ 5 ], positiveSideIndexes: [ 6 ] } -> { Vertical, negativeSideIndexes: [ 5 ], positiveSideIndexes: [ 6 ] }
{ Vertical, negativeSideIndexes: [ 1, 6 ], positiveSideIndexes: [ 2, 7, 10 ] } -> { Vertical, negativeSideIndexes: [ 1, 6 ], positiveSideIndexes: [ 2, 10 ] }
{ Vertical, negativeSideIndexes: [ 2, 7, 10 ], positiveSideIndexes: [ 3, 8, 11 ] } -> { Vertical, negativeSideIndexes: [ 2, 6 ], positiveSideIndexes: [ 2, 7 ] } -> { Vertical, negativeSideIndexes: [ 6 ], positiveSideIndexes: [ 7 ] }
{ Vertical, negativeSideIndexes: [ 8 ], positiveSideIndexes: [ 9 ] } -> removed
{ Vertical, negativeSideIndexes: [ 3, 9, 11 ], positiveSideIndexes: [ 4, 12 ] } -> { Vertical, negativeSideIndexes: [ 2, 7 ], positiveSideIndexes: [ 3, 8 ] }