[FZEditor] Grid layout resizers fixes (#4095)

* Swap resizers on drag
* Update resizers on split if existing split is used
* Fix accuracy error
* Zone ids are ordered
* Cancel merge on other actions
* Split if one of the snapped splitters is dragged
This commit is contained in:
Seraphima Zykova 2020-07-16 12:54:15 +03:00 committed by GitHub
parent 5a590512bd
commit 2282e72d03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1810 additions and 440 deletions

View File

@ -110,6 +110,8 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="DashCaseNamingPolicy.cs" />
<Compile Include="GridData.cs" />
<Compile Include="GridDragHandles.cs" />
<Compile Include="StringUtils.cs" />
<Compile Include="Converters\BooleanToBrushConverter.xaml.cs" />
<Compile Include="Converters\BooleanToIntConverter.xaml.cs" />

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,604 @@
// 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 HasSnappedNonAdjascentResizers(GridResizer resizer)
{
/**
* Resizers between zones 0,1 and 4,5 are snapped to each other and not adjascent.
* ------------------------------
* | 0 | 1 |
* ------------------------------
* | 2 | 3 |
* ------------------------------
* | 4 | 5 |
* ------------------------------
*
* Resizers between zones 0,1 and 2,3 are snapped to each other and adjascent.
* ------------------------------
* | 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 colums 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;
}
}

View File

@ -26,6 +26,7 @@ namespace FancyZonesEditor
{
InitializeComponent();
Loaded += GridEditor_Loaded;
Unloaded += GridEditor_Unloaded;
((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
}
@ -35,30 +36,11 @@ namespace FancyZonesEditor
GridLayoutModel model = (GridLayoutModel)DataContext;
if (model != null)
{
int rows = model.Rows;
int cols = model.Columns;
_rowInfo = new RowColInfo[rows];
for (int row = 0; row < rows; row++)
{
_rowInfo[row] = new RowColInfo(model.RowPercents[row]);
}
_data = new GridData(model);
_dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted);
_colInfo = new RowColInfo[cols];
for (int col = 0; col < cols; col++)
{
_colInfo[col] = new RowColInfo(model.ColumnPercents[col]);
}
int maxIndex = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
maxIndex = Math.Max(maxIndex, model.CellChildMap[row, col]);
}
}
for (int i = 0; i <= maxIndex; i++)
int zoneCount = _data.ZoneCount;
for (int i = 0; i <= zoneCount; i++)
{
AddZone();
}
@ -72,7 +54,12 @@ namespace FancyZonesEditor
}
Model.PropertyChanged += OnGridDimensionsChanged;
AddDragHandles();
_dragHandles.InitDragHandles(model);
}
private void GridEditor_Unloaded(object sender, RoutedEventArgs e)
{
gridEditorUniqueId = -1;
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
@ -116,7 +103,7 @@ namespace FancyZonesEditor
{
if (model.CellChildMap[row, col] == spliteeIndex)
{
RemoveDragHandles();
_dragHandles.RemoveDragHandles();
_startRow = _endRow = row;
_startCol = _endCol = col;
ExtendRangeToHaveEvenCellEdges();
@ -190,6 +177,8 @@ namespace FancyZonesEditor
private void OnSplit(object o, SplitEventArgs e)
{
MergeCancelClick(null, null);
UIElementCollection previewChildren = Preview.Children;
GridZone splitee = (GridZone)o;
@ -198,26 +187,10 @@ namespace FancyZonesEditor
int rows = model.Rows;
int cols = model.Columns;
int foundRow = -1;
int foundCol = -1;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (model.CellChildMap[row, col] == spliteeIndex)
{
foundRow = row;
foundCol = col;
break;
}
}
if (foundRow != -1)
{
break;
}
}
Tuple<int, int> rowCol = _data.RowColByIndex(spliteeIndex);
int foundRow = rowCol.Item1;
int foundCol = rowCol.Item2;
int newChildIndex = AddZone();
@ -231,98 +204,46 @@ namespace FancyZonesEditor
offset += Canvas.GetLeft(splitee);
int count = splitee.VerticalSnapPoints.Length;
bool foundExistingSplit = false;
int splitCol = foundCol;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkRow = foundRow;
while ((walkRow < rows) && (model.CellChildMap[walkRow, foundCol + i] == spliteeIndex))
while ((walkRow < rows) && (_data.GetIndex(walkRow, foundCol + i) == spliteeIndex))
{
model.CellChildMap[walkRow++, foundCol + i] = newChildIndex;
_data.SetIndex(walkRow++, foundCol + i, newChildIndex);
}
}
if (_colInfo[foundCol + i].End == offset)
if (_data.ColumnBottom(foundCol + i) == offset)
{
foundExistingSplit = true;
splitCol = foundCol + i;
// use existing division
}
}
if (foundExistingSplit)
{
_data.ReplaceIndicesToMaintainOrder(Preview.Children.Count);
_dragHandles.UpdateForExistingVerticalSplit(model, foundRow, splitCol);
OnGridDimensionsChanged();
return;
}
while (_colInfo[foundCol].End < offset)
while (_data.ColumnBottom(foundCol) < offset)
{
foundCol++;
}
offset -= _colInfo[foundCol].Start;
offset -= _data.ColumnTop(foundCol);
}
AddDragHandle(Orientation.Vertical, cols - 1);
cols++;
int[,] newCellChildMap = new int[rows, cols];
int[] newColPercents = new int[cols];
RowColInfo[] newColInfo = new RowColInfo[cols];
int sourceCol = 0;
for (int col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
if ((col > foundCol) && (model.CellChildMap[row, sourceCol] == spliteeIndex))
{
newCellChildMap[row, col] = newChildIndex;
}
else
{
newCellChildMap[row, col] = model.CellChildMap[row, sourceCol];
}
}
if (col != foundCol)
{
sourceCol++;
}
}
model.CellChildMap = newCellChildMap;
sourceCol = 0;
double newTotalExtent = ActualWidth - (space * (cols + 1));
for (int col = 0; col < cols; col++)
{
if (col == foundCol)
{
RowColInfo[] split = _colInfo[col].Split(offset, space);
newColInfo[col] = split[0];
newColPercents[col] = split[0].Percent;
col++;
newColInfo[col] = split[1];
newColPercents[col] = split[1].Percent;
}
else
{
newColInfo[col] = _colInfo[sourceCol];
newColInfo[col].RecalculatePercent(newTotalExtent);
newColPercents[col] = model.ColumnPercents[sourceCol];
}
sourceCol++;
}
_colInfo = newColInfo;
model.ColumnPercents = newColPercents;
model.Columns++;
_dragHandles.UpdateAfterVerticalSplit(foundCol);
_data.SplitColumn(foundCol, spliteeIndex, newChildIndex, space, offset, ActualWidth);
_dragHandles.AddDragHandle(Orientation.Vertical, foundRow, foundCol, model);
}
else
{
@ -332,158 +253,55 @@ namespace FancyZonesEditor
offset += Canvas.GetTop(splitee);
int count = splitee.HorizontalSnapPoints.Length;
bool foundExistingSplit = false;
int splitRow = foundRow;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkCol = foundCol;
while ((walkCol < cols) && (model.CellChildMap[foundRow + i, walkCol] == spliteeIndex))
while ((walkCol < cols) && (_data.GetIndex(foundRow + i, walkCol) == spliteeIndex))
{
model.CellChildMap[foundRow + i, walkCol] = newChildIndex;
_data.SetIndex(foundRow + i, walkCol++, newChildIndex);
}
}
if (_rowInfo[foundRow + i].End == offset)
if (_data.RowEnd(foundRow + i) == offset)
{
foundExistingSplit = true;
splitRow = foundRow + i;
// use existing division
}
}
if (foundExistingSplit)
{
_data.ReplaceIndicesToMaintainOrder(Preview.Children.Count);
_dragHandles.UpdateForExistingHorizontalSplit(model, splitRow, foundCol);
OnGridDimensionsChanged();
return;
}
while (_rowInfo[foundRow].End < offset)
while (_data.RowEnd(foundRow) < offset)
{
foundRow++;
}
offset -= _rowInfo[foundRow].Start;
offset -= _data.RowStart(foundRow);
}
AddDragHandle(Orientation.Horizontal, rows - 1);
rows++;
int[,] newCellChildMap = new int[rows, cols];
int[] newRowPercents = new int[rows];
RowColInfo[] newRowInfo = new RowColInfo[rows];
int sourceRow = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if ((row > foundRow) && (model.CellChildMap[sourceRow, col] == spliteeIndex))
{
newCellChildMap[row, col] = newChildIndex;
}
else
{
newCellChildMap[row, col] = model.CellChildMap[sourceRow, col];
}
_dragHandles.UpdateAfterHorizontalSplit(foundRow);
_data.SplitRow(foundRow, spliteeIndex, newChildIndex, space, offset, ActualHeight);
_dragHandles.AddDragHandle(Orientation.Horizontal, foundRow, foundCol, model);
}
if (row != foundRow)
{
sourceRow++;
}
}
model.CellChildMap = newCellChildMap;
sourceRow = 0;
double newTotalExtent = ActualHeight - (space * (rows + 1));
for (int row = 0; row < rows; row++)
{
if (row == foundRow)
{
RowColInfo[] split = _rowInfo[row].Split(offset, space);
newRowInfo[row] = split[0];
newRowPercents[row] = split[0].Percent;
row++;
newRowInfo[row] = split[1];
newRowPercents[row] = split[1].Percent;
}
else
{
newRowInfo[row] = _rowInfo[sourceRow];
newRowInfo[row].RecalculatePercent(newTotalExtent);
newRowPercents[row] = model.RowPercents[sourceRow];
}
sourceRow++;
}
_rowInfo = newRowInfo;
model.RowPercents = newRowPercents;
model.Rows++;
}
}
private void RemoveDragHandles()
{
AdornerLayer.Children.Clear();
}
private void AddDragHandles()
{
if (AdornerLayer.Children.Count == 0)
{
int interiorRows = Model.Rows - 1;
int interiorCols = Model.Columns - 1;
for (int row = 0; row < interiorRows; row++)
{
AddDragHandle(Orientation.Horizontal, row);
}
for (int col = 0; col < interiorCols; col++)
{
AddDragHandle(Orientation.Vertical, col);
}
}
}
private void AddDragHandle(Orientation orientation, int index)
{
GridResizer resizer = new GridResizer
{
Orientation = orientation,
Index = index,
Model = Model,
};
resizer.DragDelta += Resizer_DragDelta;
if (orientation == Orientation.Vertical)
{
index += Model.Rows - 1;
}
AdornerLayer.Children.Insert(index, resizer);
Size actualSize = new Size(ActualWidth, ActualHeight);
ArrangeGridRects(actualSize);
}
private void DeleteZone(int index)
{
IList<int> freeZones = Model.FreeZones;
if (freeZones.Contains(index))
{
return;
}
freeZones.Add(index);
GridZone zone = (GridZone)Preview.Children[index];
zone.Visibility = Visibility.Hidden;
zone.MinHeight = 0;
zone.MinWidth = 0;
Preview.Children.RemoveAt(index);
}
private int AddZone()
@ -544,184 +362,70 @@ namespace FancyZonesEditor
return;
}
if (model.Rows != model.RowPercents.Count || model.Columns != model.ColumnPercents.Count)
{
// Merge was not finished
return;
}
Settings settings = ((App)Application.Current).ZoneSettings;
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
int cols = model.Columns;
int rows = model.Rows;
double totalWidth = arrangeSize.Width - (spacing * (cols + 1));
double totalHeight = arrangeSize.Height - (spacing * (rows + 1));
double top = spacing;
for (int row = 0; row < rows; row++)
{
double cellHeight = _rowInfo[row].Recalculate(top, totalHeight);
top += cellHeight + spacing;
}
double left = spacing;
for (int col = 0; col < cols; col++)
{
double cellWidth = _colInfo[col].Recalculate(left, totalWidth);
left += cellWidth + spacing;
}
int zoneNumber = 1;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
int i = model.CellChildMap[row, col];
if (((row == 0) || (model.CellChildMap[row - 1, col] != i)) &&
((col == 0) || (model.CellChildMap[row, col - 1] != i)))
{
// this is not a continuation of a span
GridZone zone = (GridZone)Preview.Children[i];
left = _colInfo[col].Start;
top = _rowInfo[row].Start;
Canvas.SetLeft(zone, left);
Canvas.SetTop(zone, top);
zone.LabelID.Content = zoneNumber++;
int maxRow = row;
while (((maxRow + 1) < rows) && (model.CellChildMap[maxRow + 1, col] == i))
{
maxRow++;
}
zone.HorizontalSnapPoints = null;
if (maxRow > row)
{
zone.HorizontalSnapPoints = new double[maxRow - row];
int pointsIndex = 0;
for (int walk = row; walk < maxRow; walk++)
{
zone.HorizontalSnapPoints[pointsIndex++] = _rowInfo[walk].End + (spacing / 2) - top;
}
}
int maxCol = col;
while (((maxCol + 1) < cols) && (model.CellChildMap[row, maxCol + 1] == i))
{
maxCol++;
}
zone.VerticalSnapPoints = null;
if (maxCol > col)
{
zone.VerticalSnapPoints = new double[maxCol - col];
int pointsIndex = 0;
for (int walk = col; walk < maxCol; walk++)
{
zone.VerticalSnapPoints[pointsIndex++] = _colInfo[walk].End + (spacing / 2) - left;
}
}
zone.MinWidth = _colInfo[maxCol].End - left;
zone.MinHeight = _rowInfo[maxRow].End - top;
}
}
}
AddDragHandles();
int childIndex = 0;
UIElementCollection adornerChildren = AdornerLayer.Children;
for (int row = 0; row < rows - 1; row++)
{
GridResizer resizer = (GridResizer)adornerChildren[childIndex++];
int startCol = -1;
int endCol = cols - 1;
for (int col = 0; col < cols; col++)
{
if ((startCol == -1) && (model.CellChildMap[row, col] != model.CellChildMap[row + 1, col]))
{
startCol = col;
}
else if ((startCol != -1) && (model.CellChildMap[row, col] == model.CellChildMap[row + 1, col]))
{
endCol = col - 1;
break;
}
}
if (startCol != -1)
{
// hard coding this as (resizer.ActualHeight / 2) will still evaluate to 0 here ... a layout hasn't yet happened
Canvas.SetTop(resizer, _rowInfo[row].End + (spacing / 2) - 24);
Canvas.SetLeft(resizer, (_colInfo[endCol].End + _colInfo[startCol].Start) / 2);
}
else
{
resizer.Visibility = Visibility.Collapsed;
}
}
for (int col = 0; col < cols - 1; col++)
{
GridResizer resizer = (GridResizer)adornerChildren[childIndex++];
int startRow = -1;
int endRow = rows - 1;
for (int row = 0; row < rows; row++)
{
if ((startRow == -1) && (model.CellChildMap[row, col] != model.CellChildMap[row, col + 1]))
{
startRow = row;
}
else if ((startRow != -1) && (model.CellChildMap[row, col] == model.CellChildMap[row, col + 1]))
{
endRow = row - 1;
break;
}
}
if (startRow != -1)
{
Canvas.SetLeft(resizer, _colInfo[col].End + (spacing / 2) - 24); // hard coding this as (resizer.ActualWidth / 2) will still evaluate to 0 here ... a layout hasn't yet happened
Canvas.SetTop(resizer, (_rowInfo[endRow].End + _rowInfo[startRow].Start) / 2);
resizer.Visibility = Visibility.Visible;
}
else
{
resizer.Visibility = Visibility.Collapsed;
}
}
_data.RecalculateZones(spacing, arrangeSize);
_data.ArrangeZones(Preview.Children, spacing);
_dragHandles.InitDragHandles(model);
_data.ArrangeResizers(AdornerLayer.Children, spacing);
}
private void Resizer_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
GridResizer resizer = (GridResizer)sender;
int[] percents;
RowColInfo[] info;
int index = resizer.Index;
double delta;
MergeCancelClick(null, null);
if (resizer.Orientation == Orientation.Vertical)
GridResizer resizer = (GridResizer)sender;
double delta = (resizer.Orientation == Orientation.Vertical) ? e.HorizontalChange : e.VerticalChange;
if (delta == 0)
{
percents = Model.ColumnPercents;
info = _colInfo;
delta = e.HorizontalChange;
return;
}
GridData.ResizeInfo resizeInfo = _data.CalculateResizeInfo(resizer, delta);
if (resizeInfo.IsResizeAllowed)
{
if (_dragHandles.HasSnappedNonAdjascentResizers(resizer))
{
double spacing = 0;
Settings settings = ((App)Application.Current).ZoneSettings;
if (settings.ShowSpacing)
{
spacing = settings.Spacing;
}
_data.SplitOnDrag(resizer, delta, spacing);
_dragHandles.UpdateAfterDetach(resizer, delta);
}
else
{
percents = Model.RowPercents;
info = _rowInfo;
delta = e.VerticalChange;
_data.DragResizer(resizer, resizeInfo);
if (_data.SwapNegativePercents(resizer.Orientation, resizer.StartRow, resizer.EndRow, resizer.StartCol, resizer.EndCol))
{
_dragHandles.UpdateAfterSwap(resizer, delta);
}
}
}
double currentExtent = info[index].Extent;
double newExtent = currentExtent + delta;
int currentPercent = info[index].Percent;
int totalPercent = currentPercent + info[index + 1].Percent;
Size actualSize = new Size(ActualWidth, ActualHeight);
ArrangeGridRects(actualSize);
AdornerLayer.UpdateLayout();
}
int newPercent = (int)(currentPercent * newExtent / currentExtent);
if ((newPercent > 0) && (newPercent < totalPercent))
private void Resizer_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
GridResizer resizer = (GridResizer)sender;
int index = _data.SwappedIndexAfterResize(resizer);
if (index != -1)
{
percents[index] = info[index].Percent = newPercent;
percents[index + 1] = info[index + 1].Percent = totalPercent - newPercent;
Size actualSize = new Size(ActualWidth, ActualHeight);
ArrangeGridRects(actualSize);
}
@ -804,12 +508,12 @@ namespace FancyZonesEditor
{
if (_startRow == -1)
{
if (_rowInfo[row].End > minY)
if (_data.RowEnd(row) > minY)
{
_startRow = row;
}
}
else if (_rowInfo[row].Start > maxY)
else if (_data.RowStart(row) > maxY)
{
_endRow = row - 1;
break;
@ -825,12 +529,12 @@ namespace FancyZonesEditor
{
if (_startCol == -1)
{
if (_colInfo[col].End > minX)
if (_data.ColumnBottom(col) > minX)
{
_startCol = col;
}
}
else if (_colInfo[col].Start > maxX)
else if (_data.ColumnTop(col) > maxX)
{
_endCol = col - 1;
break;
@ -868,29 +572,21 @@ namespace FancyZonesEditor
private void MergeClick(object sender, RoutedEventArgs e)
{
GridLayoutModel model = Model;
MergePanel.Visibility = Visibility.Collapsed;
int mergedIndex = model.CellChildMap[_startRow, _startCol];
for (int row = _startRow; row <= _endRow; row++)
Action<int> deleteAction = (index) =>
{
for (int col = _startCol; col <= _endCol; col++)
{
int childIndex = model.CellChildMap[row, col];
if (childIndex != mergedIndex)
{
model.CellChildMap[row, col] = mergedIndex;
DeleteZone(childIndex);
}
}
}
DeleteZone(index);
};
_data.MergeZones(_startRow, _endRow, _startCol, _endCol, deleteAction, Preview.Children.Count);
_dragHandles.RemoveDragHandles();
_dragHandles.InitDragHandles(Model);
OnGridDimensionsChanged();
ClearSelection();
}
private void CancelClick(object sender, RoutedEventArgs e)
private void MergeCancelClick(object sender, RoutedEventArgs e)
{
MergePanel.Visibility = Visibility.Collapsed;
ClearSelection();
@ -898,7 +594,7 @@ namespace FancyZonesEditor
private void MergePanelMouseUp(object sender, MouseButtonEventArgs e)
{
CancelClick(null, null);
MergeCancelClick(null, null);
}
protected override Size ArrangeOverride(Size arrangeBounds)
@ -909,8 +605,8 @@ namespace FancyZonesEditor
return returnSize;
}
private RowColInfo[] _rowInfo;
private RowColInfo[] _colInfo;
private GridData _data;
private GridDragHandles _dragHandles;
private int _startRow = -1;
private int _endRow = -1;

View File

@ -17,7 +17,13 @@ namespace FancyZonesEditor
{
private static readonly RotateTransform _rotateTransform = new RotateTransform(90, 24, 24);
public int Index { get; set; }
public int StartRow { get; set; }
public int EndRow { get; set; }
public int StartCol { get; set; }
public int EndCol { get; set; }
public LayoutModel Model { get; set; }

View File

@ -60,10 +60,10 @@ namespace FancyZonesEditor.Models
public int[,] CellChildMap { get; set; }
// RowPercents - represents the %age height of each row in the grid
public int[] RowPercents { get; set; }
public List<int> RowPercents { get; set; }
// ColumnPercents - represents the %age width of each column in the grid
public int[] ColumnPercents { get; set; }
public List<int> ColumnPercents { get; set; }
// FreeZones (not persisted) - used to keep track of child indices that are no longer in use in the CellChildMap,
// making them candidates for re-use when it's needed to add another child
@ -85,7 +85,7 @@ namespace FancyZonesEditor.Models
{
}
public GridLayoutModel(string uuid, string name, LayoutType type, int rows, int cols, int[] rowPercents, int[] colsPercents, int[,] cellChildMap)
public GridLayoutModel(string uuid, string name, LayoutType type, int rows, int cols, List<int> rowPercents, List<int> colsPercents, int[,] cellChildMap)
: base(uuid, name, type)
{
_rows = rows;
@ -103,16 +103,16 @@ namespace FancyZonesEditor.Models
Rows = data[i++];
Columns = data[i++];
RowPercents = new int[Rows];
RowPercents = new List<int>(Rows);
for (int row = 0; row < Rows; row++)
{
RowPercents[row] = (data[i++] * 256) + data[i++];
RowPercents.Add((data[i++] * 256) + data[i++]);
}
ColumnPercents = new int[Columns];
ColumnPercents = new List<int>(Columns);
for (int col = 0; col < Columns; col++)
{
ColumnPercents[col] = (data[i++] * 256) + data[i++];
ColumnPercents.Add((data[i++] * 256) + data[i++]);
}
CellChildMap = new int[Rows, Columns];
@ -154,18 +154,18 @@ namespace FancyZonesEditor.Models
layout.CellChildMap = cellChildMap;
int[] rowPercents = new int[rows];
List<int> rowPercents = new List<int>(rows);
for (int row = 0; row < rows; row++)
{
rowPercents[row] = RowPercents[row];
rowPercents.Add(RowPercents[row]);
}
layout.RowPercents = rowPercents;
int[] colPercents = new int[cols];
List<int> colPercents = new List<int>(cols);
for (int col = 0; col < cols; col++)
{
colPercents[col] = ColumnPercents[col];
colPercents.Add(ColumnPercents[col]);
}
layout.ColumnPercents = colPercents;
@ -177,9 +177,9 @@ namespace FancyZonesEditor.Models
public int Columns { get; set; }
public int[] RowsPercentage { get; set; }
public List<int> RowsPercentage { get; set; }
public int[] ColumnsPercentage { get; set; }
public List<int> ColumnsPercentage { get; set; }
public int[][] CellChildMap { get; set; }
}

View File

@ -184,23 +184,22 @@ namespace FancyZonesEditor.Models
{
int rows = info.GetProperty("rows").GetInt32();
int columns = info.GetProperty("columns").GetInt32();
int[] rowsPercentage = new int[rows];
List<int> rowsPercentage = new List<int>(rows);
JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty("rows-percentage").EnumerateArray();
int i = 0;
while (rowsPercentageEnumerator.MoveNext())
{
rowsPercentage[i++] = rowsPercentageEnumerator.Current.GetInt32();
rowsPercentage.Add(rowsPercentageEnumerator.Current.GetInt32());
}
i = 0;
int[] columnsPercentage = new int[columns];
List<int> columnsPercentage = new List<int>(columns);
JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty("columns-percentage").EnumerateArray();
while (columnsPercentageEnumerator.MoveNext())
{
columnsPercentage[i++] = columnsPercentageEnumerator.Current.GetInt32();
columnsPercentage.Add(columnsPercentageEnumerator.Current.GetInt32());
}
i = 0;
int i = 0;
JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty("cell-child-map").EnumerateArray();
int[,] cellChildMap = new int[rows, columns];
while (cellChildMapRows.MoveNext())

View File

@ -133,14 +133,14 @@ namespace FancyZonesEditor
_columnsModel = new GridLayoutModel("Columns", LayoutType.Columns)
{
Rows = 1,
RowPercents = new int[1] { _multiplier },
RowPercents = new List<int>(1) { _multiplier },
};
DefaultModels.Add(_columnsModel);
_rowsModel = new GridLayoutModel("Rows", LayoutType.Rows)
{
Columns = 1,
ColumnPercents = new int[1] { _multiplier },
ColumnPercents = new List<int>(1) { _multiplier },
};
DefaultModels.Add(_rowsModel);
@ -308,7 +308,7 @@ namespace FancyZonesEditor
_rowsModel.CellChildMap = new int[ZoneCount, 1];
_columnsModel.CellChildMap = new int[1, ZoneCount];
_rowsModel.Rows = _columnsModel.Columns = ZoneCount;
_rowsModel.RowPercents = _columnsModel.ColumnPercents = new int[ZoneCount];
_rowsModel.RowPercents = _columnsModel.ColumnPercents = new List<int>(ZoneCount);
for (int i = 0; i < ZoneCount; i++)
{
@ -318,7 +318,7 @@ namespace FancyZonesEditor
// Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make
// the sum of all RowPercents exactly (_multiplier).
// _columnsModel is sharing the same array
_rowsModel.RowPercents[i] = ((_multiplier * (i + 1)) / ZoneCount) - ((_multiplier * i) / ZoneCount);
_rowsModel.RowPercents.Add(((_multiplier * (i + 1)) / ZoneCount) - ((_multiplier * i) / ZoneCount));
}
// Update the "Grid" Default Layout
@ -341,20 +341,20 @@ namespace FancyZonesEditor
_gridModel.Rows = rows;
_gridModel.Columns = cols;
_gridModel.RowPercents = new int[rows];
_gridModel.ColumnPercents = new int[cols];
_gridModel.RowPercents = new List<int>(rows);
_gridModel.ColumnPercents = new List<int>(cols);
_gridModel.CellChildMap = new int[rows, cols];
// Note: The following are NOT equal to _multiplier divided by rows or columns and is
// done like this to make the sum of all RowPercents exactly (_multiplier).
for (int row = 0; row < rows; row++)
{
_gridModel.RowPercents[row] = ((_multiplier * (row + 1)) / rows) - ((_multiplier * row) / rows);
_gridModel.RowPercents.Add(((_multiplier * (row + 1)) / rows) - ((_multiplier * row) / rows));
}
for (int col = 0; col < cols; col++)
{
_gridModel.ColumnPercents[col] = ((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols);
_gridModel.ColumnPercents.Add(((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols));
}
int index = ZoneCount - 1;

View File

@ -21,6 +21,14 @@ namespace FancyZonesEditor
Percent = percent;
}
public RowColInfo(RowColInfo other)
{
Percent = other.Percent;
Extent = other.Extent;
Start = other.Start;
End = other.End;
}
public RowColInfo(int index, int count)
{
Percent = (_multiplier / count) + ((index == 0) ? (_multiplier % count) : 0);