mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-12 18:29:24 +08:00
[FancyZones] Rework grid editor (#10116)
* Started rewriting * Making progress * Fix resizers not moving around * Implemented splitting, fixed some bugs * Removed more code, renamed methods * Merging zones works * Fix Shift key behavior * Added spacing (has bugs) * Implement minimum size restriction * Match preview and editor visuals * Snapping works * Show when splitting is not possible * Fix spell checker complaining * Tweak FZ Lib function computing grid zones * Fix potential crash when loading old zone layouts * Fix dead objects talking * Fix splitters being shown when they shouldn't be * Fix index numbering * Fix small glitch with the shift key * Do not snap to borders outside the zone
This commit is contained in:
parent
9a2c195f5f
commit
e586a7ad64
File diff suppressed because it is too large
Load Diff
@ -1,604 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@ -20,6 +21,9 @@ namespace FancyZonesEditor
|
||||
private const string PropertyRowsChangedID = "Rows";
|
||||
private const string PropertyColumnsChangedID = "Columns";
|
||||
private const string ObjectDependencyID = "Model";
|
||||
private const string PropertyIsShiftKeyPressedID = "IsShiftKeyPressed";
|
||||
|
||||
private const int MinZoneSize = 100;
|
||||
|
||||
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register(ObjectDependencyID, typeof(GridLayoutModel), typeof(GridEditor), new PropertyMetadata(null, OnGridDimensionsChanged));
|
||||
|
||||
@ -27,13 +31,15 @@ namespace FancyZonesEditor
|
||||
|
||||
private int gridEditorUniqueId;
|
||||
|
||||
private GridData _data;
|
||||
|
||||
public GridEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += GridEditor_Loaded;
|
||||
Unloaded += GridEditor_Unloaded;
|
||||
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
|
||||
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||
}
|
||||
|
||||
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
|
||||
@ -45,21 +51,127 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
_data = new GridData(model);
|
||||
_dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted);
|
||||
_dragHandles.InitDragHandles(model);
|
||||
|
||||
Model = model;
|
||||
Model.PropertyChanged += OnGridDimensionsChanged;
|
||||
SetupUI();
|
||||
}
|
||||
|
||||
int zoneCount = _data.ZoneCount;
|
||||
for (int i = 0; i < zoneCount; i++)
|
||||
private void PlaceResizer(GridResizer resizerThumb)
|
||||
{
|
||||
var leftZone = Preview.Children[resizerThumb.LeftReferenceZone];
|
||||
var rightZone = Preview.Children[resizerThumb.RightReferenceZone];
|
||||
var topZone = Preview.Children[resizerThumb.TopReferenceZone];
|
||||
var bottomZone = Preview.Children[resizerThumb.BottomReferenceZone];
|
||||
|
||||
double left = Canvas.GetLeft(leftZone);
|
||||
double right = Canvas.GetLeft(rightZone) + (rightZone as GridZone).MinWidth;
|
||||
|
||||
double top = Canvas.GetTop(topZone);
|
||||
double bottom = Canvas.GetTop(bottomZone) + (bottomZone as GridZone).MinHeight;
|
||||
|
||||
double x = (left + right) / 2.0;
|
||||
double y = (top + bottom) / 2.0;
|
||||
|
||||
Canvas.SetLeft(resizerThumb, x - 24);
|
||||
Canvas.SetTop(resizerThumb, y - 24);
|
||||
}
|
||||
|
||||
private void SetZonePanelSize(GridZone panel, GridData.Zone zone)
|
||||
{
|
||||
Size actualSize = WorkAreaSize();
|
||||
double spacing = Model.ShowSpacing ? Model.Spacing : 0;
|
||||
|
||||
double topSpacing = zone.Top == 0 ? spacing : spacing / 2;
|
||||
double bottomSpacing = zone.Bottom == GridData.Multiplier ? spacing : spacing / 2;
|
||||
double leftSpacing = zone.Left == 0 ? spacing : spacing / 2;
|
||||
double rightSpacing = zone.Right == GridData.Multiplier ? spacing : spacing / 2;
|
||||
|
||||
Canvas.SetTop(panel, (actualSize.Height * zone.Top / GridData.Multiplier) + topSpacing);
|
||||
Canvas.SetLeft(panel, (actualSize.Width * zone.Left / GridData.Multiplier) + leftSpacing);
|
||||
panel.MinWidth = Math.Max(1, (actualSize.Width * (zone.Right - zone.Left) / GridData.Multiplier) - leftSpacing - rightSpacing);
|
||||
panel.MinHeight = Math.Max(1, (actualSize.Height * (zone.Bottom - zone.Top) / GridData.Multiplier) - topSpacing - bottomSpacing);
|
||||
}
|
||||
|
||||
private void SetupUI()
|
||||
{
|
||||
Size actualSize = WorkAreaSize();
|
||||
|
||||
if (actualSize.Width < 1 || _data == null || Model == null)
|
||||
{
|
||||
AddZone();
|
||||
return;
|
||||
}
|
||||
|
||||
Rect workingArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||
ArrangeGridRects(actualSize);
|
||||
int spacing = Model.ShowSpacing ? Model.Spacing : 0;
|
||||
|
||||
_data.MinZoneWidth = Convert.ToInt32(GridData.Multiplier / actualSize.Width * (MinZoneSize + (2 * spacing)));
|
||||
_data.MinZoneHeight = Convert.ToInt32(GridData.Multiplier / actualSize.Height * (MinZoneSize + (2 * spacing)));
|
||||
|
||||
Preview.Children.Clear();
|
||||
AdornerLayer.Children.Clear();
|
||||
|
||||
Preview.Width = actualSize.Width;
|
||||
Preview.Height = actualSize.Height;
|
||||
|
||||
MagneticSnap snapX = new MagneticSnap(GridData.PrefixSum(Model.ColumnPercents).GetRange(1, Model.ColumnPercents.Count - 1), actualSize.Width);
|
||||
MagneticSnap snapY = new MagneticSnap(GridData.PrefixSum(Model.RowPercents).GetRange(1, Model.RowPercents.Count - 1), actualSize.Height);
|
||||
|
||||
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
|
||||
{
|
||||
// this is needed for the lambda
|
||||
int zoneIndexCopy = zoneIndex;
|
||||
|
||||
var zone = _data.Zones[zoneIndex];
|
||||
var zonePanel = new GridZone(spacing, snapX, snapY, (orientation, offset) => _data.CanSplit(zoneIndexCopy, offset, orientation), zone);
|
||||
zonePanel.UpdateShiftState(((App)Application.Current).MainWindowSettings.IsShiftKeyPressed);
|
||||
Preview.Children.Add(zonePanel);
|
||||
zonePanel.Split += OnSplit;
|
||||
zonePanel.MergeDrag += OnMergeDrag;
|
||||
zonePanel.MergeComplete += OnMergeComplete;
|
||||
SetZonePanelSize(zonePanel, zone);
|
||||
zonePanel.LabelID.Content = zoneIndex + 1;
|
||||
}
|
||||
|
||||
foreach (var resizer in _data.Resizers)
|
||||
{
|
||||
var resizerThumb = new GridResizer();
|
||||
resizerThumb.DragStarted += Resizer_DragStarted;
|
||||
resizerThumb.DragDelta += Resizer_DragDelta;
|
||||
resizerThumb.DragCompleted += Resizer_DragCompleted;
|
||||
resizerThumb.Orientation = resizer.Orientation;
|
||||
AdornerLayer.Children.Add(resizerThumb);
|
||||
|
||||
if (resizer.Orientation == Orientation.Horizontal)
|
||||
{
|
||||
resizerThumb.LeftReferenceZone = resizer.PositiveSideIndices[0];
|
||||
resizerThumb.RightReferenceZone = resizer.PositiveSideIndices.Last();
|
||||
resizerThumb.TopReferenceZone = resizer.PositiveSideIndices[0];
|
||||
resizerThumb.BottomReferenceZone = resizer.NegativeSideIndices[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
resizerThumb.LeftReferenceZone = resizer.PositiveSideIndices[0];
|
||||
resizerThumb.RightReferenceZone = resizer.NegativeSideIndices[0];
|
||||
resizerThumb.TopReferenceZone = resizer.PositiveSideIndices[0];
|
||||
resizerThumb.BottomReferenceZone = resizer.PositiveSideIndices.Last();
|
||||
}
|
||||
|
||||
PlaceResizer(resizerThumb);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSplit(object sender, SplitEventArgs args)
|
||||
{
|
||||
MergeCancelClick(null, null);
|
||||
|
||||
var zonePanel = sender as GridZone;
|
||||
int zoneIndex = Preview.Children.IndexOf(zonePanel);
|
||||
|
||||
if (_data.CanSplit(zoneIndex, args.Offset, args.Orientation))
|
||||
{
|
||||
_data.Split(zoneIndex, args.Offset, args.Orientation);
|
||||
SetupUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void GridEditor_Unloaded(object sender, RoutedEventArgs e)
|
||||
@ -67,16 +179,10 @@ namespace FancyZonesEditor
|
||||
gridEditorUniqueId = -1;
|
||||
}
|
||||
|
||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
private Size WorkAreaSize()
|
||||
{
|
||||
Rect workingArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||
|
||||
// Only enter if this is the newest instance
|
||||
if (actualSize.Width > 0 && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
||||
{
|
||||
ArrangeGridRects(actualSize);
|
||||
}
|
||||
return new Size(workingArea.Width, workingArea.Height);
|
||||
}
|
||||
|
||||
public GridLayoutModel Model
|
||||
@ -90,258 +196,6 @@ namespace FancyZonesEditor
|
||||
get { return Preview; }
|
||||
}
|
||||
|
||||
private void OnFullSplit(object o, SplitEventArgs e)
|
||||
{
|
||||
UIElementCollection previewChildren = Preview.Children;
|
||||
UIElement splitee = (UIElement)o;
|
||||
|
||||
GridLayoutModel model = Model;
|
||||
int spliteeIndex = previewChildren.IndexOf(splitee);
|
||||
|
||||
int rows = model.Rows;
|
||||
int cols = model.Columns;
|
||||
_startRow = -1;
|
||||
_startCol = -1;
|
||||
|
||||
for (int row = rows - 1; row >= 0; row--)
|
||||
{
|
||||
for (int col = cols - 1; col >= 0; col--)
|
||||
{
|
||||
if (model.CellChildMap[row, col] == spliteeIndex)
|
||||
{
|
||||
_dragHandles.RemoveDragHandles();
|
||||
_startRow = _endRow = row;
|
||||
_startCol = _endCol = col;
|
||||
ExtendRangeToHaveEvenCellEdges();
|
||||
|
||||
for (row = _startRow; row <= _endRow; row++)
|
||||
{
|
||||
for (col = _startCol; col <= _endCol; col++)
|
||||
{
|
||||
if ((row != _startRow) || (col != _startCol))
|
||||
{
|
||||
model.CellChildMap[row, col] = AddZone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnGridDimensionsChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtendRangeToHaveEvenCellEdges()
|
||||
{
|
||||
// As long as there is an edge of the 2D range such that some zone crosses its boundary, extend
|
||||
// that boundary. A single pass is not enough, a while loop is needed. This results in the unique
|
||||
// smallest rectangle containing the initial range such that no zone is "broken", meaning that
|
||||
// some part of it is inside the 2D range, and some part is outside.
|
||||
GridLayoutModel model = Model;
|
||||
bool possiblyBroken = true;
|
||||
|
||||
while (possiblyBroken)
|
||||
{
|
||||
possiblyBroken = false;
|
||||
|
||||
for (int col = _startCol; col <= _endCol; col++)
|
||||
{
|
||||
if (_startRow > 0 && model.CellChildMap[_startRow - 1, col] == model.CellChildMap[_startRow, col])
|
||||
{
|
||||
_startRow--;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_endRow < model.Rows - 1 && model.CellChildMap[_endRow + 1, col] == model.CellChildMap[_endRow, col])
|
||||
{
|
||||
_endRow++;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = _startRow; row <= _endRow; row++)
|
||||
{
|
||||
if (_startCol > 0 && model.CellChildMap[row, _startCol - 1] == model.CellChildMap[row, _startCol])
|
||||
{
|
||||
_startCol--;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_endCol < model.Columns - 1 && model.CellChildMap[row, _endCol + 1] == model.CellChildMap[row, _endCol])
|
||||
{
|
||||
_endCol++;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSplit(object o, SplitEventArgs e)
|
||||
{
|
||||
MergeCancelClick(null, null);
|
||||
|
||||
UIElementCollection previewChildren = Preview.Children;
|
||||
GridZone splitee = (GridZone)o;
|
||||
|
||||
int spliteeIndex = previewChildren.IndexOf(splitee);
|
||||
GridLayoutModel model = Model;
|
||||
|
||||
int rows = model.Rows;
|
||||
int cols = model.Columns;
|
||||
|
||||
Tuple<int, int> rowCol = _data.RowColByIndex(spliteeIndex);
|
||||
int foundRow = rowCol.Item1;
|
||||
int foundCol = rowCol.Item2;
|
||||
|
||||
int newChildIndex = AddZone();
|
||||
|
||||
double offset = e.Offset;
|
||||
double space = e.Space;
|
||||
|
||||
if (e.Orientation == Orientation.Vertical)
|
||||
{
|
||||
if (splitee.VerticalSnapPoints != null)
|
||||
{
|
||||
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) && (_data.GetIndex(walkRow, foundCol + i) == spliteeIndex))
|
||||
{
|
||||
_data.SetIndex(walkRow++, foundCol + i, newChildIndex);
|
||||
}
|
||||
}
|
||||
|
||||
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 (_data.ColumnBottom(foundCol) < offset)
|
||||
{
|
||||
foundCol++;
|
||||
}
|
||||
|
||||
offset -= _data.ColumnTop(foundCol);
|
||||
}
|
||||
|
||||
_dragHandles.UpdateAfterVerticalSplit(foundCol);
|
||||
_data.SplitColumn(foundCol, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Width);
|
||||
_dragHandles.AddDragHandle(Orientation.Vertical, foundRow, foundCol, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Horizontal
|
||||
if (splitee.HorizontalSnapPoints != null)
|
||||
{
|
||||
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) && (_data.GetIndex(foundRow + i, walkCol) == spliteeIndex))
|
||||
{
|
||||
_data.SetIndex(foundRow + i, walkCol++, newChildIndex);
|
||||
}
|
||||
}
|
||||
|
||||
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 (_data.RowEnd(foundRow) < offset)
|
||||
{
|
||||
foundRow++;
|
||||
}
|
||||
|
||||
offset -= _data.RowStart(foundRow);
|
||||
}
|
||||
|
||||
_dragHandles.UpdateAfterHorizontalSplit(foundRow);
|
||||
_data.SplitRow(foundRow, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Height);
|
||||
_dragHandles.AddDragHandle(Orientation.Horizontal, foundRow, foundCol, model);
|
||||
}
|
||||
|
||||
var workArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workArea.Width, workArea.Height);
|
||||
ArrangeGridRects(actualSize);
|
||||
}
|
||||
|
||||
private void DeleteZone(int index)
|
||||
{
|
||||
Preview.Children.RemoveAt(index);
|
||||
}
|
||||
|
||||
private int AddZone()
|
||||
{
|
||||
GridZone zone;
|
||||
if (Model != null)
|
||||
{
|
||||
IList<int> freeZones = Model.FreeZones;
|
||||
|
||||
// first check free list
|
||||
if (freeZones.Count > 0)
|
||||
{
|
||||
int freeIndex = freeZones[0];
|
||||
freeZones.RemoveAt(0);
|
||||
zone = (GridZone)Preview.Children[freeIndex];
|
||||
zone.Visibility = Visibility.Visible;
|
||||
return freeIndex;
|
||||
}
|
||||
|
||||
zone = new GridZone(Model.ShowSpacing ? Model.Spacing : 0);
|
||||
zone.Split += OnSplit;
|
||||
zone.MergeDrag += OnMergeDrag;
|
||||
zone.MergeComplete += OnMergeComplete;
|
||||
zone.FullSplit += OnFullSplit;
|
||||
Preview.Children.Add(zone);
|
||||
return Preview.Children.Count - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
// Only enter if this is the newest instance
|
||||
@ -351,230 +205,201 @@ namespace FancyZonesEditor
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if ((e.PropertyName == PropertyIsShiftKeyPressedID) && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
||||
{
|
||||
foreach (var child in Preview.Children)
|
||||
{
|
||||
var zone = child as GridZone;
|
||||
zone.UpdateShiftState(((App)Application.Current).MainWindowSettings.IsShiftKeyPressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGridDimensionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((GridEditor)d).OnGridDimensionsChanged();
|
||||
((GridEditor)d).SetupUI();
|
||||
}
|
||||
|
||||
private void OnGridDimensionsChanged()
|
||||
{
|
||||
Rect workingArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||
if (actualSize.Width > 0)
|
||||
{
|
||||
ArrangeGridRects(actualSize);
|
||||
}
|
||||
SetupUI();
|
||||
}
|
||||
|
||||
private void ArrangeGridRects(Size arrangeSize)
|
||||
private double _dragX = 0;
|
||||
private double _dragY = 0;
|
||||
|
||||
private void Resizer_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
|
||||
{
|
||||
var workArea = App.Overlay.WorkArea;
|
||||
Preview.Width = workArea.Width;
|
||||
Preview.Height = workArea.Height;
|
||||
|
||||
GridLayoutModel model = Model;
|
||||
if (model == null || _data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.Rows != model.RowPercents.Count || model.Columns != model.ColumnPercents.Count)
|
||||
{
|
||||
// Merge was not finished
|
||||
return;
|
||||
}
|
||||
|
||||
int spacing = model.ShowSpacing ? model.Spacing : 0;
|
||||
|
||||
_data.RecalculateZones(spacing, arrangeSize);
|
||||
_data.ArrangeZones(Preview.Children, spacing);
|
||||
_dragHandles.InitDragHandles(model);
|
||||
_data.ArrangeResizers(AdornerLayer.Children, spacing);
|
||||
_dragX = 0;
|
||||
_dragY = 0;
|
||||
}
|
||||
|
||||
private void Resizer_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
|
||||
{
|
||||
MergeCancelClick(null, null);
|
||||
|
||||
GridResizer resizer = (GridResizer)sender;
|
||||
_dragX += e.HorizontalChange;
|
||||
_dragY += e.VerticalChange;
|
||||
|
||||
double delta = (resizer.Orientation == Orientation.Vertical) ? e.HorizontalChange : e.VerticalChange;
|
||||
if (delta == 0)
|
||||
GridResizer resizer = (GridResizer)sender;
|
||||
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
|
||||
|
||||
Size actualSize = WorkAreaSize();
|
||||
|
||||
int delta;
|
||||
|
||||
if (resizer.Orientation == Orientation.Vertical)
|
||||
{
|
||||
return;
|
||||
delta = Convert.ToInt32(_dragX / actualSize.Width * GridData.Multiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
delta = Convert.ToInt32(_dragY / actualSize.Height * GridData.Multiplier);
|
||||
}
|
||||
|
||||
GridData.ResizeInfo resizeInfo = _data.CalculateResizeInfo(resizer, delta);
|
||||
if (resizeInfo.IsResizeAllowed)
|
||||
if (_data.CanDrag(resizerIndex, delta))
|
||||
{
|
||||
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
|
||||
// Just update the UI, don't tell _data
|
||||
if (resizer.Orientation == Orientation.Vertical)
|
||||
{
|
||||
double spacing = 0;
|
||||
GridLayoutModel model = Model;
|
||||
if (model.ShowSpacing)
|
||||
_data.Resizers[resizerIndex].PositiveSideIndices.ForEach((zoneIndex) =>
|
||||
{
|
||||
spacing = model.Spacing;
|
||||
}
|
||||
var zone = Preview.Children[zoneIndex];
|
||||
Canvas.SetLeft(zone, Canvas.GetLeft(zone) + e.HorizontalChange);
|
||||
(zone as GridZone).MinWidth -= e.HorizontalChange;
|
||||
});
|
||||
|
||||
_data.SplitOnDrag(resizer, delta, spacing);
|
||||
_dragHandles.UpdateAfterDetach(resizer, delta);
|
||||
_data.Resizers[resizerIndex].NegativeSideIndices.ForEach((zoneIndex) =>
|
||||
{
|
||||
var zone = Preview.Children[zoneIndex];
|
||||
Canvas.SetRight(zone, Canvas.GetRight(zone) + e.HorizontalChange);
|
||||
(zone as GridZone).MinWidth += e.HorizontalChange;
|
||||
});
|
||||
|
||||
Canvas.SetLeft(resizer, Canvas.GetLeft(resizer) + e.HorizontalChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
_data.DragResizer(resizer, resizeInfo);
|
||||
if (_data.SwapNegativePercents(resizer.Orientation, resizer.StartRow, resizer.EndRow, resizer.StartCol, resizer.EndCol))
|
||||
_data.Resizers[resizerIndex].PositiveSideIndices.ForEach((zoneIndex) =>
|
||||
{
|
||||
_dragHandles.UpdateAfterSwap(resizer, delta);
|
||||
var zone = Preview.Children[zoneIndex];
|
||||
Canvas.SetTop(zone, Canvas.GetTop(zone) + e.VerticalChange);
|
||||
(zone as GridZone).MinHeight -= e.VerticalChange;
|
||||
});
|
||||
|
||||
_data.Resizers[resizerIndex].NegativeSideIndices.ForEach((zoneIndex) =>
|
||||
{
|
||||
var zone = Preview.Children[zoneIndex];
|
||||
Canvas.SetBottom(zone, Canvas.GetBottom(zone) + e.VerticalChange);
|
||||
(zone as GridZone).MinHeight += e.VerticalChange;
|
||||
});
|
||||
|
||||
Canvas.SetTop(resizer, Canvas.GetTop(resizer) + e.VerticalChange);
|
||||
}
|
||||
|
||||
foreach (var child in AdornerLayer.Children)
|
||||
{
|
||||
GridResizer resizerThumb = child as GridResizer;
|
||||
if (resizerThumb != resizer)
|
||||
{
|
||||
PlaceResizer(resizerThumb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect workingArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||
ArrangeGridRects(actualSize);
|
||||
AdornerLayer.UpdateLayout();
|
||||
else
|
||||
{
|
||||
// Undo changes
|
||||
_dragX -= e.HorizontalChange;
|
||||
_dragY -= e.VerticalChange;
|
||||
}
|
||||
}
|
||||
|
||||
private void Resizer_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
|
||||
{
|
||||
GridResizer resizer = (GridResizer)sender;
|
||||
int index = _data.SwappedIndexAfterResize(resizer);
|
||||
if (index != -1)
|
||||
{
|
||||
Rect workingArea = App.Overlay.WorkArea;
|
||||
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||
ArrangeGridRects(actualSize);
|
||||
}
|
||||
}
|
||||
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
|
||||
Size actualSize = WorkAreaSize();
|
||||
|
||||
private Point _startDragPos = new Point(-1, -1);
|
||||
double pixelDelta = resizer.Orientation == Orientation.Vertical ?
|
||||
_dragX / actualSize.Width * GridData.Multiplier :
|
||||
_dragY / actualSize.Height * GridData.Multiplier;
|
||||
|
||||
_data.Drag(resizerIndex, Convert.ToInt32(pixelDelta));
|
||||
|
||||
SetupUI();
|
||||
}
|
||||
|
||||
private void OnMergeComplete(object o, MouseButtonEventArgs e)
|
||||
{
|
||||
Point mousePoint = e.GetPosition(Preview);
|
||||
_startDragPos = new Point(-1, -1);
|
||||
_inMergeDrag = false;
|
||||
|
||||
int mergedIndex = Model.CellChildMap[_startRow, _startCol];
|
||||
|
||||
for (int row = _startRow; row <= _endRow; row++)
|
||||
var selectedIndices = new List<int>();
|
||||
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count; zoneIndex++)
|
||||
{
|
||||
for (int col = _startCol; col <= _endCol; col++)
|
||||
if ((Preview.Children[zoneIndex] as GridZone).IsSelected)
|
||||
{
|
||||
if (Model.CellChildMap[row, col] != mergedIndex)
|
||||
{
|
||||
// selection is more than one cell, merge is valid
|
||||
MergePanel.Visibility = Visibility.Visible;
|
||||
Canvas.SetTop(MergeButtons, mousePoint.Y);
|
||||
Canvas.SetLeft(MergeButtons, mousePoint.X);
|
||||
return;
|
||||
}
|
||||
selectedIndices.Add(zoneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// merge is only one zone. cancel merge;
|
||||
ClearSelection();
|
||||
if (selectedIndices.Count <= 1)
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
Point mousePoint = e.GetPosition(Preview);
|
||||
MergePanel.Visibility = Visibility.Visible;
|
||||
Canvas.SetLeft(MergeButtons, mousePoint.X);
|
||||
Canvas.SetTop(MergeButtons, mousePoint.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _inMergeDrag;
|
||||
private Point _mergeDragStart;
|
||||
|
||||
private void OnMergeDrag(object o, MouseEventArgs e)
|
||||
{
|
||||
if (_startDragPos.X == -1)
|
||||
Point dragPosition = e.GetPosition(Preview);
|
||||
Size actualSize = WorkAreaSize();
|
||||
|
||||
if (!_inMergeDrag)
|
||||
{
|
||||
_startDragPos = e.GetPosition(Preview);
|
||||
_inMergeDrag = true;
|
||||
_mergeDragStart = dragPosition;
|
||||
}
|
||||
|
||||
GridLayoutModel model = Model;
|
||||
// Find the new zone, if any
|
||||
int dataLowX = Convert.ToInt32(Math.Min(_mergeDragStart.X, dragPosition.X) / actualSize.Width * GridData.Multiplier);
|
||||
int dataHighX = Convert.ToInt32(Math.Max(_mergeDragStart.X, dragPosition.X) / actualSize.Width * GridData.Multiplier);
|
||||
int dataLowY = Convert.ToInt32(Math.Min(_mergeDragStart.Y, dragPosition.Y) / actualSize.Height * GridData.Multiplier);
|
||||
int dataHighY = Convert.ToInt32(Math.Max(_mergeDragStart.Y, dragPosition.Y) / actualSize.Height * GridData.Multiplier);
|
||||
|
||||
if (_startDragPos.X != -1)
|
||||
var selectedIndices = new List<int>();
|
||||
|
||||
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
|
||||
{
|
||||
Point dragPos = e.GetPosition(Preview);
|
||||
_startRow = -1;
|
||||
_endRow = -1;
|
||||
_startCol = -1;
|
||||
_endCol = -1;
|
||||
var zoneData = _data.Zones[zoneIndex];
|
||||
|
||||
int rows = model.Rows;
|
||||
int cols = model.Columns;
|
||||
bool selected = Math.Max(zoneData.Left, dataLowX) <= Math.Min(zoneData.Right, dataHighX) &&
|
||||
Math.Max(zoneData.Top, dataLowY) <= Math.Min(zoneData.Bottom, dataHighY);
|
||||
|
||||
double minX, maxX;
|
||||
if (dragPos.X < _startDragPos.X)
|
||||
// Check whether the zone intersects the selected rectangle
|
||||
(Preview.Children[zoneIndex] as GridZone).IsSelected = selected;
|
||||
|
||||
if (selected)
|
||||
{
|
||||
minX = dragPos.X;
|
||||
maxX = _startDragPos.X;
|
||||
selectedIndices.Add(zoneIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
minX = _startDragPos.X;
|
||||
maxX = dragPos.X;
|
||||
}
|
||||
|
||||
double minY, maxY;
|
||||
if (dragPos.Y < _startDragPos.Y)
|
||||
{
|
||||
minY = dragPos.Y;
|
||||
maxY = _startDragPos.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
minY = _startDragPos.Y;
|
||||
maxY = dragPos.Y;
|
||||
}
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
if (_startRow == -1)
|
||||
{
|
||||
if (_data.RowEnd(row) > minY)
|
||||
{
|
||||
_startRow = row;
|
||||
}
|
||||
}
|
||||
else if (_data.RowStart(row) > maxY)
|
||||
{
|
||||
_endRow = row - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_startRow >= 0) && (_endRow == -1))
|
||||
{
|
||||
_endRow = rows - 1;
|
||||
}
|
||||
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
if (_startCol == -1)
|
||||
{
|
||||
if (_data.ColumnBottom(col) > minX)
|
||||
{
|
||||
_startCol = col;
|
||||
}
|
||||
}
|
||||
else if (_data.ColumnTop(col) > maxX)
|
||||
{
|
||||
_endCol = col - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_startCol >= 0) && (_endCol == -1))
|
||||
{
|
||||
_endCol = cols - 1;
|
||||
}
|
||||
|
||||
ExtendRangeToHaveEvenCellEdges();
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
((GridZone)Preview.Children[model.CellChildMap[row, col]]).IsSelected = (row >= _startRow) && (row <= _endRow) && (col >= _startCol) && (col <= _endCol);
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
OnPreviewMouseMove(e);
|
||||
// Compute the closure
|
||||
_data.MergeClosureIndices(selectedIndices).ForEach((zoneIndex) =>
|
||||
{
|
||||
(Preview.Children[zoneIndex] as GridZone).IsSelected = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void ClearSelection()
|
||||
@ -583,22 +408,27 @@ namespace FancyZonesEditor
|
||||
{
|
||||
((GridZone)zone).IsSelected = false;
|
||||
}
|
||||
|
||||
_inMergeDrag = false;
|
||||
}
|
||||
|
||||
private void MergeClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MergePanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
Action<int> deleteAction = (index) =>
|
||||
{
|
||||
DeleteZone(index);
|
||||
};
|
||||
_data.MergeZones(_startRow, _endRow, _startCol, _endCol, deleteAction, Preview.Children.Count);
|
||||
_dragHandles.RemoveDragHandles();
|
||||
_dragHandles.InitDragHandles(Model);
|
||||
var selectedIndices = new List<int>();
|
||||
|
||||
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
|
||||
{
|
||||
if ((Preview.Children[zoneIndex] as GridZone).IsSelected)
|
||||
{
|
||||
selectedIndices.Add(zoneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
OnGridDimensionsChanged();
|
||||
ClearSelection();
|
||||
_data.DoMerge(selectedIndices);
|
||||
SetupUI();
|
||||
}
|
||||
|
||||
private void MergeCancelClick(object sender, RoutedEventArgs e)
|
||||
@ -615,17 +445,9 @@ namespace FancyZonesEditor
|
||||
protected override Size ArrangeOverride(Size arrangeBounds)
|
||||
{
|
||||
Size returnSize = base.ArrangeOverride(arrangeBounds);
|
||||
ArrangeGridRects(arrangeBounds);
|
||||
SetupUI();
|
||||
|
||||
return returnSize;
|
||||
}
|
||||
|
||||
private GridData _data;
|
||||
private GridDragHandles _dragHandles;
|
||||
|
||||
private int _startRow = -1;
|
||||
private int _endRow = -1;
|
||||
private int _startCol = -1;
|
||||
private int _endCol = -1;
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,13 @@ namespace FancyZonesEditor
|
||||
{
|
||||
private static readonly RotateTransform _rotateTransform = new RotateTransform(90, 24, 24);
|
||||
|
||||
public int StartRow { get; set; }
|
||||
public int LeftReferenceZone { get; set; }
|
||||
|
||||
public int EndRow { get; set; }
|
||||
public int RightReferenceZone { get; set; }
|
||||
|
||||
public int StartCol { get; set; }
|
||||
public int TopReferenceZone { get; set; }
|
||||
|
||||
public int EndCol { get; set; }
|
||||
public int BottomReferenceZone { get; set; }
|
||||
|
||||
public LayoutModel Model { get; set; }
|
||||
|
||||
|
@ -20,28 +20,28 @@ namespace FancyZonesEditor
|
||||
// Non-localizable strings
|
||||
private const string ObjectDependencyID = "IsSelected";
|
||||
private const string GridZoneBackgroundBrushID = "GridZoneBackgroundBrush";
|
||||
private const string PropertyIsShiftKeyPressedID = "IsShiftKeyPressed";
|
||||
private const string SecondaryForegroundBrushID = "SecondaryForegroundBrush";
|
||||
|
||||
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(GridZone), new PropertyMetadata(false, OnSelectionChanged));
|
||||
|
||||
public event SplitEventHandler Split;
|
||||
|
||||
public event SplitEventHandler FullSplit;
|
||||
|
||||
public event MouseEventHandler MergeDrag;
|
||||
|
||||
public event MouseButtonEventHandler MergeComplete;
|
||||
|
||||
public double[] VerticalSnapPoints { get; set; }
|
||||
|
||||
public double[] HorizontalSnapPoints { get; set; }
|
||||
|
||||
private readonly Rectangle _splitter;
|
||||
private bool _switchOrientation;
|
||||
private bool _switchOrientation = false;
|
||||
private Point _lastPos = new Point(-1, -1);
|
||||
private int _snappedPositionX;
|
||||
private int _snappedPositionY;
|
||||
private Point _mouseDownPos = new Point(-1, -1);
|
||||
private bool _inMergeDrag;
|
||||
private Orientation _splitOrientation;
|
||||
private MagneticSnap _snapX;
|
||||
private MagneticSnap _snapY;
|
||||
private Func<Orientation, int, bool> _canSplit;
|
||||
private bool _hovering;
|
||||
private GridData.Zone _zone;
|
||||
|
||||
private static void OnSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
@ -59,7 +59,7 @@ namespace FancyZonesEditor
|
||||
set { SetValue(IsSelectedProperty, value); }
|
||||
}
|
||||
|
||||
public GridZone(int spacing)
|
||||
public GridZone(int spacing, MagneticSnap snapX, MagneticSnap snapY, Func<Orientation, int, bool> canSplit, GridData.Zone zone)
|
||||
{
|
||||
InitializeComponent();
|
||||
OnSelectionChanged();
|
||||
@ -69,11 +69,14 @@ namespace FancyZonesEditor
|
||||
};
|
||||
Body.Children.Add(_splitter);
|
||||
|
||||
Spacing = spacing;
|
||||
SplitterThickness = Math.Max(spacing, 1);
|
||||
|
||||
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||
SizeChanged += GridZone_SizeChanged;
|
||||
|
||||
_snapX = snapX;
|
||||
_snapY = snapY;
|
||||
_canSplit = canSplit;
|
||||
_zone = zone;
|
||||
}
|
||||
|
||||
private void GridZone_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
@ -82,91 +85,76 @@ namespace FancyZonesEditor
|
||||
HeightLabel.Text = Math.Round(ActualHeight).ToString();
|
||||
}
|
||||
|
||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
public void UpdateShiftState(bool shiftState)
|
||||
{
|
||||
if (e.PropertyName == PropertyIsShiftKeyPressedID)
|
||||
{
|
||||
_switchOrientation = ((App)Application.Current).MainWindowSettings.IsShiftKeyPressed;
|
||||
if (_lastPos.X != -1)
|
||||
{
|
||||
UpdateSplitter();
|
||||
}
|
||||
}
|
||||
}
|
||||
_switchOrientation = shiftState;
|
||||
|
||||
protected override Size ArrangeOverride(Size size)
|
||||
{
|
||||
_splitOrientation = (size.Width > size.Height) ? Orientation.Vertical : Orientation.Horizontal;
|
||||
return base.ArrangeOverride(size);
|
||||
if (_lastPos.X != -1)
|
||||
{
|
||||
UpdateSplitter();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsVerticalSplit
|
||||
{
|
||||
get
|
||||
{
|
||||
bool isVertical = _splitOrientation == Orientation.Vertical;
|
||||
if (_switchOrientation)
|
||||
{
|
||||
isVertical = !isVertical;
|
||||
}
|
||||
|
||||
return isVertical;
|
||||
}
|
||||
get => (ActualWidth > ActualHeight) ^ _switchOrientation;
|
||||
}
|
||||
|
||||
private int Spacing { get; set; }
|
||||
|
||||
private int SplitterThickness { get; set; }
|
||||
|
||||
private void UpdateSplitter()
|
||||
{
|
||||
if (!_hovering)
|
||||
{
|
||||
_splitter.Fill = Brushes.Transparent;
|
||||
return;
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
|
||||
if (IsVerticalSplit)
|
||||
{
|
||||
double bodyWidth = Body.ActualWidth;
|
||||
double pos = _lastPos.X - (SplitterThickness / 2);
|
||||
if (pos < 0)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else if (pos > (bodyWidth - SplitterThickness))
|
||||
{
|
||||
pos = bodyWidth - SplitterThickness;
|
||||
}
|
||||
double pos = _snapX.DataToPixelWithoutSnapping(_snappedPositionX) - Canvas.GetLeft(this) - (SplitterThickness / 2);
|
||||
pos = Math.Clamp(pos, 0, bodyWidth - SplitterThickness);
|
||||
|
||||
Canvas.SetLeft(_splitter, pos);
|
||||
Canvas.SetTop(_splitter, 0);
|
||||
_splitter.MinWidth = SplitterThickness;
|
||||
_splitter.MinHeight = Body.ActualHeight;
|
||||
|
||||
enabled = _canSplit(Orientation.Vertical, _snappedPositionX);
|
||||
}
|
||||
else
|
||||
{
|
||||
double bodyHeight = Body.ActualHeight;
|
||||
double pos = _lastPos.Y - (SplitterThickness / 2);
|
||||
if (pos < 0)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else if (pos > (bodyHeight - SplitterThickness))
|
||||
{
|
||||
pos = bodyHeight - SplitterThickness;
|
||||
}
|
||||
double pos = _snapY.DataToPixelWithoutSnapping(_snappedPositionY) - Canvas.GetTop(this) - (SplitterThickness / 2);
|
||||
pos = Math.Clamp(pos, 0, bodyHeight - SplitterThickness);
|
||||
|
||||
Canvas.SetLeft(_splitter, 0);
|
||||
Canvas.SetTop(_splitter, pos);
|
||||
_splitter.MinWidth = Body.ActualWidth;
|
||||
_splitter.MinHeight = SplitterThickness;
|
||||
|
||||
enabled = _canSplit(Orientation.Horizontal, _snappedPositionY);
|
||||
}
|
||||
|
||||
Brush disabledBrush = App.Current.Resources[SecondaryForegroundBrushID] as SolidColorBrush;
|
||||
Brush enabledBrush = SystemParameters.WindowGlassBrush; // Active Accent color
|
||||
_splitter.Fill = enabled ? enabledBrush : disabledBrush;
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(MouseEventArgs e)
|
||||
{
|
||||
_splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
|
||||
base.OnMouseEnter(e);
|
||||
_hovering = true;
|
||||
UpdateSplitter();
|
||||
_splitter.Fill = SystemParameters.WindowGlassBrush;
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(MouseEventArgs e)
|
||||
{
|
||||
_splitter.Fill = Brushes.Transparent;
|
||||
_hovering = false;
|
||||
UpdateSplitter();
|
||||
base.OnMouseLeave(e);
|
||||
}
|
||||
|
||||
@ -185,38 +173,8 @@ namespace FancyZonesEditor
|
||||
else
|
||||
{
|
||||
_lastPos = e.GetPosition(Body);
|
||||
|
||||
if (IsVerticalSplit)
|
||||
{
|
||||
if (VerticalSnapPoints != null)
|
||||
{
|
||||
int thickness = SplitterThickness;
|
||||
foreach (double snapPoint in VerticalSnapPoints)
|
||||
{
|
||||
if (Math.Abs(_lastPos.X - snapPoint) <= (thickness * 2))
|
||||
{
|
||||
_lastPos.X = snapPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// horizontal split
|
||||
if (HorizontalSnapPoints != null)
|
||||
{
|
||||
int thickness = SplitterThickness;
|
||||
foreach (double snapPoint in HorizontalSnapPoints)
|
||||
{
|
||||
if (Math.Abs(_lastPos.Y - snapPoint) <= (thickness * 2))
|
||||
{
|
||||
_lastPos.Y = snapPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_snappedPositionX = _snapX.PixelToDataWithSnapping(e.GetPosition(Parent as GridEditor).X, _zone.Left, _zone.Right);
|
||||
_snappedPositionY = _snapY.PixelToDataWithSnapping(e.GetPosition(Parent as GridEditor).Y, _zone.Top, _zone.Bottom);
|
||||
|
||||
if (_mouseDownPos.X == -1)
|
||||
{
|
||||
@ -257,11 +215,11 @@ namespace FancyZonesEditor
|
||||
{
|
||||
if (IsVerticalSplit)
|
||||
{
|
||||
DoSplit(Orientation.Vertical, _lastPos.X - (thickness / 2));
|
||||
DoSplit(Orientation.Vertical, _snappedPositionX);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoSplit(Orientation.Horizontal, _lastPos.Y - (thickness / 2));
|
||||
DoSplit(Orientation.Horizontal, _snappedPositionY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,19 +238,9 @@ namespace FancyZonesEditor
|
||||
MergeComplete?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void DoSplit(Orientation orientation, double offset)
|
||||
private void DoSplit(Orientation orientation, int offset)
|
||||
{
|
||||
Split?.Invoke(this, new SplitEventArgs(orientation, offset, Spacing));
|
||||
}
|
||||
|
||||
private void FullSplit_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DoFullSplit();
|
||||
}
|
||||
|
||||
private void DoFullSplit()
|
||||
{
|
||||
FullSplit?.Invoke(this, new SplitEventArgs());
|
||||
Split?.Invoke(this, new SplitEventArgs(orientation, offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
@ -27,7 +26,6 @@ namespace FancyZonesEditor
|
||||
|
||||
public static readonly DependencyProperty IsActualSizeProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(LayoutPreview), new PropertyMetadata(false));
|
||||
private LayoutModel _model;
|
||||
private List<Int32Rect> _zones = new List<Int32Rect>();
|
||||
|
||||
public bool IsActualSize
|
||||
{
|
||||
@ -82,11 +80,6 @@ namespace FancyZonesEditor
|
||||
RenderPreview();
|
||||
}
|
||||
|
||||
public Int32Rect[] GetZoneRects()
|
||||
{
|
||||
return _zones.ToArray();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_model = (LayoutModel)DataContext;
|
||||
@ -105,8 +98,6 @@ namespace FancyZonesEditor
|
||||
Body.RowDefinitions.Clear();
|
||||
Body.ColumnDefinitions.Clear();
|
||||
|
||||
_zones.Clear();
|
||||
|
||||
if (_model is GridLayoutModel gridModel)
|
||||
{
|
||||
RenderGridPreview(gridModel);
|
||||
@ -121,32 +112,12 @@ namespace FancyZonesEditor
|
||||
{
|
||||
int rows = grid.Rows;
|
||||
int cols = grid.Columns;
|
||||
double spacing = grid.ShowSpacing ? grid.Spacing : 0;
|
||||
|
||||
RowColInfo[] rowInfo = (from percent in grid.RowPercents
|
||||
select new RowColInfo(percent)).ToArray();
|
||||
|
||||
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
|
||||
select new RowColInfo(percent)).ToArray();
|
||||
|
||||
int spacing = grid.ShowSpacing ? grid.Spacing : 0;
|
||||
var rowData = GridData.PrefixSum(grid.RowPercents);
|
||||
var columnData = GridData.PrefixSum(grid.ColumnPercents);
|
||||
|
||||
var workArea = App.Overlay.WorkArea;
|
||||
double width = workArea.Width - (spacing * (cols + 1));
|
||||
double height = workArea.Height - (spacing * (rows + 1));
|
||||
|
||||
double top = spacing;
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
double cellHeight = rowInfo[row].Recalculate(top, height);
|
||||
top += cellHeight + spacing;
|
||||
}
|
||||
|
||||
double left = spacing;
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
double cellWidth = colInfo[col].Recalculate(left, width);
|
||||
left += cellWidth + spacing;
|
||||
}
|
||||
|
||||
Viewbox viewbox = new Viewbox
|
||||
{
|
||||
@ -170,10 +141,8 @@ namespace FancyZonesEditor
|
||||
{
|
||||
// this is not a continuation of a span
|
||||
Border rect = new Border();
|
||||
left = colInfo[col].Start;
|
||||
top = rowInfo[row].Start;
|
||||
Canvas.SetTop(rect, top);
|
||||
Canvas.SetLeft(rect, left);
|
||||
double left = columnData[col] * workArea.Width / GridData.Multiplier;
|
||||
double top = rowData[row] * workArea.Height / GridData.Multiplier;
|
||||
|
||||
int maxRow = row;
|
||||
while (((maxRow + 1) < rows) && (grid.CellChildMap[maxRow + 1, col] == childIndex))
|
||||
@ -187,12 +156,21 @@ namespace FancyZonesEditor
|
||||
maxCol++;
|
||||
}
|
||||
|
||||
rect.Width = Math.Max(0, colInfo[maxCol].End - left);
|
||||
rect.Height = Math.Max(0, rowInfo[maxRow].End - top);
|
||||
double right = columnData[maxCol + 1] * workArea.Width / GridData.Multiplier;
|
||||
double bottom = rowData[maxRow + 1] * workArea.Height / GridData.Multiplier;
|
||||
|
||||
left += col == 0 ? spacing : spacing / 2;
|
||||
right -= maxCol == cols - 1 ? spacing : spacing / 2;
|
||||
top += row == 0 ? spacing : spacing / 2;
|
||||
bottom -= maxRow == rows - 1 ? spacing : spacing / 2;
|
||||
|
||||
Canvas.SetTop(rect, top);
|
||||
Canvas.SetLeft(rect, left);
|
||||
rect.Width = Math.Max(1, right - left);
|
||||
rect.Height = Math.Max(1, bottom - top);
|
||||
|
||||
rect.Style = (Style)FindResource("GridLayoutActualScalePreviewStyle");
|
||||
frame.Children.Add(rect);
|
||||
_zones.Add(new Int32Rect(
|
||||
(int)left, (int)top, (int)rect.Width, (int)rect.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
// 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.Linq;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
public class MagneticSnap
|
||||
{
|
||||
private List<int> _keyPoints;
|
||||
private double _workAreaSize;
|
||||
|
||||
private const int MagnetZoneMaxSize = GridData.Multiplier / 12;
|
||||
|
||||
public MagneticSnap(List<int> keyPoints, double workAreaSize)
|
||||
{
|
||||
_keyPoints = keyPoints;
|
||||
_workAreaSize = workAreaSize;
|
||||
}
|
||||
|
||||
public int PixelToDataWithSnapping(double pixel, int low, int high)
|
||||
{
|
||||
var keyPoints = _keyPoints.Where(x => low < x && x < high).ToList();
|
||||
var magnetZoneSizes = new List<int>();
|
||||
|
||||
for (int i = 0; i < keyPoints.Count; i++)
|
||||
{
|
||||
int previous = i == 0 ? low : keyPoints[i - 1];
|
||||
int next = i == keyPoints.Count - 1 ? high : keyPoints[i + 1];
|
||||
magnetZoneSizes.Add(Math.Min(keyPoints[i] - previous, Math.Min(next - keyPoints[i], MagnetZoneMaxSize)) / 2);
|
||||
}
|
||||
|
||||
int data = Convert.ToInt32(pixel / _workAreaSize * GridData.Multiplier);
|
||||
data = Math.Clamp(data, low, high);
|
||||
int result;
|
||||
int snapId = -1;
|
||||
|
||||
for (int i = 0; i < keyPoints.Count; ++i)
|
||||
{
|
||||
if (Math.Abs(data - keyPoints[i]) <= magnetZoneSizes[i])
|
||||
{
|
||||
snapId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (snapId == -1)
|
||||
{
|
||||
result = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
int deadZoneWidth = (magnetZoneSizes[snapId] + 1) / 2;
|
||||
if (Math.Abs(data - keyPoints[snapId]) <= deadZoneWidth)
|
||||
{
|
||||
result = keyPoints[snapId];
|
||||
}
|
||||
else if (data < keyPoints[snapId])
|
||||
{
|
||||
result = data + (data - (keyPoints[snapId] - magnetZoneSizes[snapId]));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = data - ((keyPoints[snapId] + magnetZoneSizes[snapId]) - data);
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Clamp(result, low, high);
|
||||
}
|
||||
|
||||
public double DataToPixelWithoutSnapping(int data)
|
||||
{
|
||||
return _workAreaSize * data / GridData.Multiplier;
|
||||
}
|
||||
}
|
||||
}
|
@ -120,11 +120,6 @@ namespace FancyZonesEditor.Models
|
||||
|
||||
private int _spacing = LayoutSettings.DefaultSpacing;
|
||||
|
||||
// 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
|
||||
// TODO: do I need FreeZones on the data model? - I think I do
|
||||
public IList<int> FreeZones { get; } = new List<int>();
|
||||
|
||||
public GridLayoutModel()
|
||||
: base()
|
||||
{
|
||||
|
@ -363,46 +363,5 @@ namespace FancyZonesEditor
|
||||
Monitors.Add(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
public Int32Rect[] GetZoneRects()
|
||||
{
|
||||
if (_editor != null)
|
||||
{
|
||||
if (_editor is GridEditor gridEditor)
|
||||
{
|
||||
return ZoneRectsFromPanel(gridEditor.PreviewPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CanvasEditor
|
||||
return ZoneRectsFromPanel(((CanvasEditor)_editor).Preview);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// One of the predefined zones (neither grid or canvas editor used).
|
||||
return _layoutPreview.GetZoneRects();
|
||||
}
|
||||
}
|
||||
|
||||
private Int32Rect[] ZoneRectsFromPanel(Panel previewPanel)
|
||||
{
|
||||
// TODO: the ideal here is that the ArrangeRects logic is entirely inside the model, so we don't have to walk the UIElement children to get the rect info
|
||||
int count = previewPanel.Children.Count;
|
||||
Int32Rect[] zones = new Int32Rect[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
FrameworkElement child = (FrameworkElement)previewPanel.Children[i];
|
||||
Point topLeft = child.TransformToAncestor(previewPanel).Transform(default);
|
||||
|
||||
zones[i].X = (int)topLeft.X;
|
||||
zones[i].Y = (int)topLeft.Y;
|
||||
zones[i].Width = (int)child.ActualWidth;
|
||||
zones[i].Height = (int)child.ActualHeight;
|
||||
}
|
||||
|
||||
return zones;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
// 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.
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
public class RowColInfo
|
||||
{
|
||||
private const int _multiplier = 10000;
|
||||
|
||||
public double Extent { get; set; }
|
||||
|
||||
public double Start { get; set; }
|
||||
|
||||
public double End { get; set; }
|
||||
|
||||
public int Percent { get; set; }
|
||||
|
||||
public RowColInfo(int percent)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public double Recalculate(double start, double totalExtent)
|
||||
{
|
||||
Start = start;
|
||||
Extent = totalExtent * Percent / _multiplier;
|
||||
End = Start + Extent;
|
||||
return Extent;
|
||||
}
|
||||
|
||||
public void RecalculatePercent(double newTotalExtent)
|
||||
{
|
||||
Percent = (int)(Extent * _multiplier / newTotalExtent);
|
||||
}
|
||||
|
||||
public RowColInfo[] Split(double offset, double space)
|
||||
{
|
||||
RowColInfo[] info = new RowColInfo[2];
|
||||
|
||||
double totalExtent = Extent * _multiplier / Percent;
|
||||
totalExtent -= space;
|
||||
|
||||
int percent0 = (int)(offset * _multiplier / totalExtent);
|
||||
int percent1 = (int)((Extent - space - offset) * _multiplier / totalExtent);
|
||||
|
||||
info[0] = new RowColInfo(percent0);
|
||||
info[1] = new RowColInfo(percent1);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,18 +13,15 @@ namespace FancyZonesEditor
|
||||
{
|
||||
}
|
||||
|
||||
public SplitEventArgs(Orientation orientation, double offset, double thickness)
|
||||
public SplitEventArgs(Orientation orientation, int offset)
|
||||
{
|
||||
Orientation = orientation;
|
||||
Offset = offset;
|
||||
Space = thickness;
|
||||
}
|
||||
|
||||
public Orientation Orientation { get; }
|
||||
|
||||
public double Offset { get; }
|
||||
|
||||
public double Space { get; }
|
||||
public int Offset { get; }
|
||||
}
|
||||
|
||||
public delegate void SplitEventHandler(object sender, SplitEventArgs args);
|
||||
|
@ -825,8 +825,8 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
|
||||
|
||||
bool ZoneSet::CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing)
|
||||
{
|
||||
long totalWidth = workArea.width() - (spacing * (gridLayoutInfo.columns() + 1));
|
||||
long totalHeight = workArea.height() - (spacing * (gridLayoutInfo.rows() + 1));
|
||||
long totalWidth = workArea.width();
|
||||
long totalHeight = workArea.height();
|
||||
struct Info
|
||||
{
|
||||
long Extent;
|
||||
@ -841,18 +841,18 @@ bool ZoneSet::CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutI
|
||||
int totalPercents = 0;
|
||||
for (int row = 0; row < gridLayoutInfo.rows(); row++)
|
||||
{
|
||||
rowInfo[row].Start = totalPercents * totalHeight / C_MULTIPLIER + (row + 1) * spacing;
|
||||
rowInfo[row].Start = totalPercents * totalHeight / C_MULTIPLIER;
|
||||
totalPercents += gridLayoutInfo.rowsPercents()[row];
|
||||
rowInfo[row].End = totalPercents * totalHeight / C_MULTIPLIER + (row + 1) * spacing;
|
||||
rowInfo[row].End = totalPercents * totalHeight / C_MULTIPLIER;
|
||||
rowInfo[row].Extent = rowInfo[row].End - rowInfo[row].Start;
|
||||
}
|
||||
|
||||
totalPercents = 0;
|
||||
for (int col = 0; col < gridLayoutInfo.columns(); col++)
|
||||
{
|
||||
columnInfo[col].Start = totalPercents * totalWidth / C_MULTIPLIER + (col + 1) * spacing;
|
||||
columnInfo[col].Start = totalPercents * totalWidth / C_MULTIPLIER;
|
||||
totalPercents += gridLayoutInfo.columnsPercents()[col];
|
||||
columnInfo[col].End = totalPercents * totalWidth / C_MULTIPLIER + (col + 1) * spacing;
|
||||
columnInfo[col].End = totalPercents * totalWidth / C_MULTIPLIER;
|
||||
columnInfo[col].Extent = columnInfo[col].End - columnInfo[col].Start;
|
||||
}
|
||||
|
||||
@ -881,6 +881,11 @@ bool ZoneSet::CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutI
|
||||
long right = columnInfo[maxCol].End;
|
||||
long bottom = rowInfo[maxRow].End;
|
||||
|
||||
top += row == 0 ? spacing : spacing / 2;
|
||||
bottom -= row == gridLayoutInfo.rows() - 1 ? spacing : spacing / 2;
|
||||
left += col == 0 ? spacing : spacing / 2;
|
||||
right -= col == gridLayoutInfo.columns() - 1 ? spacing : spacing / 2;
|
||||
|
||||
auto zone = MakeZone(RECT{ left, top, right, bottom }, i);
|
||||
if (zone)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user