Files
PowerToys/src/modules/fancyzones/editor/FancyZonesEditor/GridDragHandles.cs
Josh Soref e68999ea0d spelling: adjacent
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-30 14:39:10 -04:00

605 lines
22 KiB
C#

// 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.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
public class GridDragHandles
{
public GridDragHandles(UIElementCollection resizers, Action<object, DragDeltaEventArgs> dragDelta, Action<object, DragCompletedEventArgs> dragCompleted)
{
_resizers = resizers;
_dragDelta = dragDelta;
_dragCompleted = dragCompleted;
}
public void InitDragHandles(GridLayoutModel model)
{
if (_resizers.Count == 0)
{
int[,] indices = model.CellChildMap;
// horizontal resizers
for (int row = 0; row < model.Rows - 1; row++)
{
for (int col = 0; col < model.Columns; col++)
{
if (indices[row, col] != indices[row + 1, col])
{
int endCol = col + 1;
while (endCol < model.Columns && indices[row, endCol] != indices[row + 1, endCol])
{
endCol++;
}
AddDragHandle(Orientation.Horizontal, row, row + 1, col, endCol, row);
col = endCol - 1;
}
}
}
// vertical resizers
for (int col = 0; col < model.Columns - 1; col++)
{
for (int row = 0; row < model.Rows; row++)
{
if (indices[row, col] != indices[row, col + 1])
{
int endRow = row + 1;
while (endRow < model.Rows && indices[endRow, col] != indices[endRow, col + 1])
{
endRow++;
}
AddDragHandle(Orientation.Vertical, row, endRow, col, col + 1, col + model.Rows - 1);
row = endRow - 1;
}
}
}
}
}
public void AddDragHandle(Orientation orientation, int foundRow, int foundCol, GridLayoutModel model)
{
int[,] indices = model.CellChildMap;
int endRow = foundRow + 1;
while (endRow < model.Rows && indices[endRow, foundCol] == indices[endRow - 1, foundCol])
{
endRow++;
}
int endCol = foundCol + 1;
while (endCol < model.Columns && indices[foundRow, endCol] == indices[foundRow, endCol - 1])
{
endCol++;
}
int index = (orientation == Orientation.Horizontal) ? foundRow : foundCol + model.Rows - 1;
AddDragHandle(orientation, foundRow, endRow, foundCol, endCol, index);
}
public void AddDragHandle(Orientation orientation, int rowStart, int rowEnd, int colStart, int colEnd, int index)
{
GridResizer resizer = new GridResizer
{
Orientation = orientation,
StartRow = rowStart,
EndRow = rowEnd,
StartCol = colStart,
EndCol = colEnd,
};
resizer.DragDelta += (obj, eventArgs) => _dragDelta(obj, eventArgs);
resizer.DragCompleted += (obj, eventArgs) => _dragCompleted(obj, eventArgs);
if (index > _resizers.Count)
{
index = _resizers.Count;
}
_resizers.Insert(index, resizer);
}
public void UpdateForExistingVerticalSplit(GridLayoutModel model, int foundRow, int splitCol)
{
Func<GridResizer, bool> cmpr = (GridResizer resizer) =>
{
return resizer.Orientation == Orientation.Vertical && resizer.StartCol == splitCol;
};
Func<GridResizer, bool> endCmpr = (GridResizer resizer) =>
{
return resizer.EndRow == foundRow;
};
Func<GridResizer, bool> startCmpr = (GridResizer resizer) =>
{
return resizer.StartRow == foundRow + 1;
};
if (!UpdateDragHandlerForExistingSplit(Orientation.Vertical, cmpr, endCmpr, startCmpr))
{
AddDragHandle(Orientation.Vertical, foundRow, splitCol, model);
}
}
public void UpdateForExistingHorizontalSplit(GridLayoutModel model, int splitRow, int foundCol)
{
Func<GridResizer, bool> cmpr = (GridResizer resizer) =>
{
return resizer.Orientation == Orientation.Horizontal && resizer.StartRow == splitRow;
};
Func<GridResizer, bool> endCmpr = (GridResizer resizer) =>
{
return resizer.EndCol == foundCol;
};
Func<GridResizer, bool> startCmpr = (GridResizer resizer) =>
{
return resizer.StartCol == foundCol + 1;
};
if (!UpdateDragHandlerForExistingSplit(Orientation.Horizontal, cmpr, endCmpr, startCmpr))
{
AddDragHandle(Orientation.Horizontal, splitRow, foundCol, model);
}
}
/**
* Has to be called on split before adding new drag handle
*/
public void UpdateAfterVerticalSplit(int foundCol)
{
foreach (GridResizer r in _resizers)
{
if (r.StartCol > foundCol || (r.StartCol == foundCol && r.Orientation == Orientation.Vertical))
{
r.StartCol++;
}
if (r.EndCol > foundCol)
{
r.EndCol++;
}
}
}
/**
* Has to be called on split before adding new drag handle
*/
public void UpdateAfterHorizontalSplit(int foundRow)
{
foreach (GridResizer r in _resizers)
{
if (r.StartRow > foundRow || (r.StartRow == foundRow && r.Orientation == Orientation.Horizontal))
{
r.StartRow++;
}
if (r.EndRow > foundRow)
{
r.EndRow++;
}
}
}
public void UpdateAfterSwap(GridResizer resizer, double delta)
{
Orientation orientation = resizer.Orientation;
bool isHorizontal = orientation == Orientation.Horizontal;
bool isDeltaNegative = delta < 0;
List<GridResizer> swappedResizers = new List<GridResizer>();
if (isDeltaNegative)
{
DecreaseResizerValues(resizer, orientation);
}
else
{
IncreaseResizerValues(resizer, orientation);
}
// same orientation resizers update
foreach (GridResizer r in _resizers)
{
if (r.Orientation == orientation)
{
if ((isHorizontal && r.StartRow == resizer.StartRow && r.StartCol != resizer.StartCol) ||
(!isHorizontal && r.StartCol == resizer.StartCol && r.StartRow != resizer.StartRow))
{
if (isDeltaNegative)
{
IncreaseResizerValues(r, orientation);
}
else
{
DecreaseResizerValues(r, orientation);
}
swappedResizers.Add(r);
}
}
}
// different orientation resizers update
foreach (GridResizer r in _resizers)
{
if (r.Orientation != resizer.Orientation)
{
if (isHorizontal)
{
// vertical resizers corresponding to dragged resizer
if (r.StartCol >= resizer.StartCol && r.EndCol < resizer.EndCol)
{
if (r.StartRow == resizer.StartRow + 2 && isDeltaNegative)
{
r.StartRow--;
}
if (r.EndRow == resizer.EndRow + 1 && isDeltaNegative)
{
r.EndRow--;
}
if (r.StartRow == resizer.StartRow && !isDeltaNegative)
{
r.StartRow++;
}
if (r.EndRow == resizer.EndRow - 1 && !isDeltaNegative)
{
r.EndRow++;
}
}
else
{
// vertical resizers corresponding to swapped resizers
foreach (GridResizer sr in swappedResizers)
{
if (r.StartCol >= sr.StartCol && r.EndCol <= sr.EndCol)
{
if (r.StartRow == resizer.StartRow + 1 && isDeltaNegative)
{
r.StartRow++;
}
if (r.EndRow == resizer.EndRow && isDeltaNegative)
{
r.EndRow++;
}
if (r.StartRow == resizer.StartRow + 1 && !isDeltaNegative)
{
r.StartRow--;
}
if (r.EndRow == resizer.EndRow && !isDeltaNegative)
{
r.EndRow--;
}
}
}
}
}
else
{
// horizontal resizers corresponding to dragged resizer
if (r.StartRow >= resizer.StartRow && r.EndRow < resizer.EndRow)
{
if (r.StartCol == resizer.StartCol + 3 && isDeltaNegative)
{
r.StartCol--;
}
if (r.EndCol == resizer.EndCol + 1 && isDeltaNegative)
{
r.EndCol--;
}
if (r.StartCol == resizer.StartCol && !isDeltaNegative)
{
r.StartCol++;
}
if (r.EndCol == resizer.EndCol - 1 && !isDeltaNegative)
{
r.EndCol++;
}
}
else
{
// horizontal resizers corresponding to swapped resizers
foreach (GridResizer sr in swappedResizers)
{
if (r.StartRow >= sr.StartRow && r.EndRow <= sr.EndRow)
{
if (r.StartCol == resizer.StartCol + 1 && isDeltaNegative)
{
r.StartCol++;
}
if (r.EndCol == resizer.EndCol && isDeltaNegative)
{
r.EndCol++;
}
if (r.StartCol == resizer.StartCol + 1 && !isDeltaNegative)
{
r.StartCol--;
}
if (r.EndCol == resizer.EndCol && !isDeltaNegative)
{
r.EndCol--;
}
}
}
}
}
}
}
}
public void UpdateAfterDetach(GridResizer resizer, double delta)
{
bool isDeltaNegative = delta < 0;
Orientation orientation = resizer.Orientation;
foreach (GridResizer r in _resizers)
{
bool notEqual = r.StartRow != resizer.StartRow || r.EndRow != resizer.EndRow || r.StartCol != resizer.StartCol || r.EndCol != resizer.EndCol;
if (r.Orientation == orientation && notEqual)
{
if (orientation == Orientation.Horizontal)
{
if (r.StartRow > resizer.StartRow || (r.StartRow == resizer.StartRow && isDeltaNegative))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow || (r.EndRow == resizer.EndRow && isDeltaNegative))
{
r.EndRow++;
}
}
else
{
if (r.StartCol > resizer.StartCol || (r.StartCol == resizer.StartCol && isDeltaNegative))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol || (r.EndCol == resizer.EndCol && isDeltaNegative))
{
r.EndCol++;
}
}
}
}
if (!isDeltaNegative)
{
IncreaseResizerValues(resizer, orientation);
}
foreach (GridResizer r in _resizers)
{
if (r.Orientation != orientation)
{
if (orientation == Orientation.Vertical)
{
if (isDeltaNegative)
{
bool isRowNonAdjacent = r.EndRow < resizer.StartRow || r.StartRow > resizer.EndRow;
if (r.StartCol > resizer.StartCol + 1 || (r.StartCol == resizer.StartCol + 1 && isRowNonAdjacent))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol || (r.EndCol == resizer.EndCol && isRowNonAdjacent))
{
r.EndCol++;
}
}
else
{
if (r.StartCol > resizer.StartCol || (r.StartCol == resizer.StartCol && r.StartRow >= resizer.StartRow && r.EndRow <= resizer.EndRow))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol - 1 || (r.EndCol == resizer.EndCol - 1 && r.StartRow >= resizer.StartRow && r.EndRow <= resizer.EndRow))
{
r.EndCol++;
}
}
}
else
{
if (isDeltaNegative)
{
bool isColNonAdjacent = r.EndCol < resizer.StartCol || r.StartCol > resizer.EndCol;
if (r.StartRow > resizer.StartRow + 1 || (r.StartRow == resizer.StartRow + 1 && isColNonAdjacent))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow || (r.EndRow == resizer.EndRow && isColNonAdjacent))
{
r.EndRow++;
}
}
else
{
if (r.StartRow > resizer.StartRow || (r.StartRow == resizer.StartRow && r.StartCol >= resizer.StartCol && r.EndCol <= resizer.EndCol))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow - 1 || (r.EndRow == resizer.EndRow - 1 && r.StartCol >= resizer.StartCol && r.EndCol <= resizer.EndCol))
{
r.EndRow++;
}
}
}
}
}
}
public void RemoveDragHandles()
{
_resizers.Clear();
}
public bool HasSnappedNonAdjacentResizers(GridResizer resizer)
{
/**
* Resizers between zones 0,1 and 4,5 are snapped to each other and not adjacent.
* ------------------------------
* | 0 | 1 |
* ------------------------------
* | 2 | 3 |
* ------------------------------
* | 4 | 5 |
* ------------------------------
*
* Resizers between zones 0,1 and 2,3 are snapped to each other and adjacent.
* ------------------------------
* | 0 | 1 |
* ------------------------------
* | 2 | 3 |
* ------------------------------
* | 4 | 5 |
* ------------------------------
*
* Vertical resizers should have same StartColumn and different StartRow.
* Horizontal resizers should have same StartRow and different StartColumn.
* Difference between rows or columns should be more than 1.
*/
foreach (GridResizer r in _resizers)
{
if (r.Orientation == resizer.Orientation)
{
bool isHorizontalSnapped = resizer.Orientation == Orientation.Horizontal && r.StartRow == resizer.StartRow && (Math.Abs(resizer.StartCol - r.StartCol) > 1);
bool isVerticalSnapped = resizer.Orientation == Orientation.Vertical && r.StartCol == resizer.StartCol && (Math.Abs(resizer.StartRow - r.StartRow) > 1);
if (isHorizontalSnapped || isVerticalSnapped)
{
return true;
}
}
}
return false;
}
private static void IncreaseResizerValues(GridResizer resizer, Orientation orientation)
{
if (orientation == Orientation.Vertical)
{
resizer.StartCol++;
resizer.EndCol++;
}
else
{
resizer.StartRow++;
resizer.EndRow++;
}
}
private static void DecreaseResizerValues(GridResizer resizer, Orientation orientation)
{
if (orientation == Orientation.Vertical)
{
resizer.StartCol--;
resizer.EndCol--;
}
else
{
resizer.StartRow--;
resizer.EndRow--;
}
}
private bool UpdateDragHandlerForExistingSplit(Orientation orientation, Func<GridResizer, bool> cmpr, Func<GridResizer, bool> endCmpr, Func<GridResizer, bool> startCmpr)
{
bool updCurrentResizers = false;
GridResizer leftNeighbour = null;
GridResizer rightNeighbour = null;
for (int i = 0; i < _resizers.Count && (leftNeighbour == null || rightNeighbour == null); i++)
{
GridResizer resizer = (GridResizer)_resizers[i];
if (cmpr(resizer))
{
if (leftNeighbour == null && endCmpr(resizer))
{
leftNeighbour = resizer;
updCurrentResizers = true;
}
if (rightNeighbour == null && startCmpr(resizer))
{
rightNeighbour = resizer;
updCurrentResizers = true;
}
}
}
if (updCurrentResizers)
{
if (leftNeighbour != null && rightNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
leftNeighbour.EndRow = rightNeighbour.EndRow;
}
else
{
leftNeighbour.EndCol = rightNeighbour.EndCol;
}
_resizers.Remove(rightNeighbour);
}
else if (leftNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
leftNeighbour.EndRow++;
}
else
{
leftNeighbour.EndCol++;
}
}
else if (rightNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
rightNeighbour.StartRow--;
}
else
{
rightNeighbour.StartCol--;
}
}
}
return updCurrentResizers;
}
private readonly UIElementCollection _resizers;
private readonly Action<object, DragDeltaEventArgs> _dragDelta;
private readonly Action<object, DragCompletedEventArgs> _dragCompleted;
}
}