mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 11:16:51 +02:00
FancyZones and Shortcut Guide initial commit
Co-authored-by: Alexis Campailla <alexis@janeasystems.com> Co-authored-by: Bret Anderson <bretan@microsoft.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com> Co-authored-by: Jaime Bernardo <jaime@janeasystems.com> Co-authored-by: Jeff Bogdan <jeffbog@microsoft.com> Co-authored-by: March Rogers <marchr@microsoft.com> Co-authored-by: Mike Harsh <mharsh@microsoft.com> Co-authored-by: Nachum Bundak <Nachum.Bundak@microsoft.com> Co-authored-by: Oliver Jones <ojones@microsoft.com> Co-authored-by: Patrick Little <plittle@microsoft.com>
This commit is contained in:
committed by
Bartosz Sosnowski
parent
10c5396099
commit
8431b80e48
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Converters;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace FancyZonesEditor.Models
|
||||
{
|
||||
// CanvasLayoutModel
|
||||
// Free form Layout Model, which specifies independent zone rects
|
||||
public class CanvasLayoutModel : LayoutModel
|
||||
{
|
||||
public CanvasLayoutModel(ushort version, string name, ushort id, byte[] data) : base(name, id)
|
||||
{
|
||||
if (version == c_latestVersion)
|
||||
{
|
||||
Load(data);
|
||||
}
|
||||
}
|
||||
|
||||
public CanvasLayoutModel(string name, ushort id, int referenceWidth, int referenceHeight) : base(name, id)
|
||||
{
|
||||
// Initialize Reference Size
|
||||
_referenceWidth = referenceWidth;
|
||||
_referenceHeight = referenceHeight;
|
||||
}
|
||||
|
||||
public CanvasLayoutModel(string name, ushort id) : base(name, id) { }
|
||||
public CanvasLayoutModel(string name) : base(name) { }
|
||||
public CanvasLayoutModel() : base() { }
|
||||
|
||||
// ReferenceWidth - the reference width for the layout rect that all Zones are relative to
|
||||
public int ReferenceWidth
|
||||
{
|
||||
get { return _referenceWidth; }
|
||||
set
|
||||
{
|
||||
if (_referenceWidth != value)
|
||||
{
|
||||
_referenceWidth = value;
|
||||
FirePropertyChanged("ReferenceWidth");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _referenceWidth;
|
||||
|
||||
// ReferenceHeight - the reference height for the layout rect that all Zones are relative to
|
||||
public int ReferenceHeight
|
||||
{
|
||||
get { return _referenceHeight; }
|
||||
set
|
||||
{
|
||||
if (_referenceHeight != value)
|
||||
{
|
||||
_referenceHeight = value;
|
||||
FirePropertyChanged("ReferenceHeight");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _referenceHeight;
|
||||
|
||||
// Zones - the list of all zones in this layout, described as independent rectangles
|
||||
public IList<Int32Rect> Zones { get { return _zones; } }
|
||||
private IList<Int32Rect> _zones = new List<Int32Rect>();
|
||||
|
||||
// RemoveZoneAt
|
||||
// Removes the specified index from the Zones list, and fires a property changed notification for the Zones property
|
||||
public void RemoveZoneAt(int index)
|
||||
{
|
||||
Zones.RemoveAt(index);
|
||||
FirePropertyChanged("Zones");
|
||||
}
|
||||
|
||||
// AddZone
|
||||
// Adds the specified Zone to the end of the Zones list, and fires a property changed notification for the Zones property
|
||||
public void AddZone(Int32Rect zone)
|
||||
{
|
||||
Zones.Add(zone);
|
||||
FirePropertyChanged("Zones");
|
||||
}
|
||||
|
||||
private void Load(byte[] data)
|
||||
{
|
||||
// Initialize this CanvasLayoutModel based on the given persistence data
|
||||
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
|
||||
int i = 5;
|
||||
_referenceWidth = data[i++] * 256 + data[i++];
|
||||
_referenceHeight = data[i++] * 256 + data[i++];
|
||||
|
||||
int count = data[i++];
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
_zones.Add(new Int32Rect(
|
||||
data[i++] * 256 + data[i++],
|
||||
data[i++] * 256 + data[i++],
|
||||
data[i++] * 256 + data[i++],
|
||||
data[i++] * 256 + data[i++]));
|
||||
}
|
||||
}
|
||||
|
||||
// Clone
|
||||
// Implements the LayoutModel.Clone abstract method
|
||||
// Clones the data from this CanvasLayoutModel to a new CanvasLayoutModel
|
||||
public override LayoutModel Clone()
|
||||
{
|
||||
CanvasLayoutModel layout = new CanvasLayoutModel(Name);
|
||||
layout.ReferenceHeight = ReferenceHeight;
|
||||
layout.ReferenceWidth = ReferenceWidth;
|
||||
|
||||
foreach(Int32Rect zone in Zones)
|
||||
{
|
||||
layout.Zones.Add(zone);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
// GetPersistData
|
||||
// Implements the LayoutModel.GetPersistData abstract method
|
||||
// Returns the state of this GridLayoutModel in persisted format
|
||||
protected override byte[] GetPersistData()
|
||||
{
|
||||
byte[] data = new byte[10 + (_zones.Count * 8)];
|
||||
int i = 0;
|
||||
|
||||
// Common persisted values between all layout types
|
||||
data[i++] = (byte)(c_latestVersion / 256);
|
||||
data[i++] = (byte)(c_latestVersion % 256);
|
||||
data[i++] = 1; // LayoutModelType: 1 == CanvasLayoutModel
|
||||
data[i++] = (byte)(Id / 256);
|
||||
data[i++] = (byte)(Id % 256);
|
||||
// End common
|
||||
|
||||
data[i++] = (byte)(_referenceWidth / 256);
|
||||
data[i++] = (byte)(_referenceWidth % 256);
|
||||
data[i++] = (byte)(_referenceHeight / 256);
|
||||
data[i++] = (byte)(_referenceHeight % 256);
|
||||
data[i++] = (byte)_zones.Count;
|
||||
|
||||
foreach (Int32Rect rect in _zones)
|
||||
{
|
||||
data[i++] = (byte)(rect.X / 256);
|
||||
data[i++] = (byte)(rect.X % 256);
|
||||
|
||||
data[i++] = (byte)(rect.Y / 256);
|
||||
data[i++] = (byte)(rect.Y % 256);
|
||||
|
||||
data[i++] = (byte)(rect.Width / 256);
|
||||
data[i++] = (byte)(rect.Width % 256);
|
||||
|
||||
data[i++] = (byte)(rect.Height / 256);
|
||||
data[i++] = (byte)(rect.Height % 256);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ushort c_latestVersion = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace FancyZonesEditor.Models
|
||||
{
|
||||
// GridLayoutModel
|
||||
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
|
||||
public class GridLayoutModel : LayoutModel
|
||||
{
|
||||
public GridLayoutModel() : base() { }
|
||||
public GridLayoutModel(string name) : base(name) { }
|
||||
public GridLayoutModel(string name, ushort id) : base(name, id) { }
|
||||
public GridLayoutModel(ushort version, string name, ushort id, byte[] data) : base(name, id)
|
||||
{
|
||||
if (version == c_latestVersion)
|
||||
{
|
||||
Reload(data);
|
||||
}
|
||||
}
|
||||
// Rows - number of rows in the Grid
|
||||
public int Rows
|
||||
{
|
||||
get { return _rows; }
|
||||
set
|
||||
{
|
||||
if (_rows != value)
|
||||
{
|
||||
_rows = value;
|
||||
FirePropertyChanged("Rows");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _rows = 1;
|
||||
|
||||
// Columns - number of columns in the Grid
|
||||
public int Columns
|
||||
{
|
||||
get { return _cols; }
|
||||
set
|
||||
{
|
||||
if (_cols != value)
|
||||
{
|
||||
_cols = value;
|
||||
FirePropertyChanged("Columns");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _cols = 1;
|
||||
|
||||
// CellChildMap - represents which "children" belong in which grid cells;
|
||||
// shows spanning children by the same index appearing in adjacent cells
|
||||
// TODO: ideally no setter here - this means moving logic like "split" over to model
|
||||
public int[,] CellChildMap { get { return _cellChildMap; } set { _cellChildMap = value; } }
|
||||
private int[,] _cellChildMap;
|
||||
|
||||
// RowPercents - represents the %age height of each row in the grid
|
||||
public int[] RowPercents { get { return _rowPercents; } set { _rowPercents = value; } }
|
||||
private int[] _rowPercents;
|
||||
|
||||
// ColumnPercents - represents the %age width of each column in the grid
|
||||
public int[] ColumnPercents { get { return _colPercents; } set { _colPercents = value; } }
|
||||
private int[] _colPercents;
|
||||
|
||||
// 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 { return _freeZones; } }
|
||||
private IList<int> _freeZones = new List<int>();
|
||||
|
||||
public void Reload(byte[] data)
|
||||
{
|
||||
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
|
||||
int i = 5;
|
||||
|
||||
Rows = data[i++];
|
||||
Columns = data[i++];
|
||||
|
||||
_rowPercents = new int[Rows];
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
_rowPercents[row] = data[i++]*256 + data[i++];
|
||||
}
|
||||
|
||||
_colPercents = new int[Columns];
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
_colPercents[col] = data[i++]*256 + data[i++];
|
||||
}
|
||||
|
||||
_cellChildMap = new int[Rows, Columns];
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
_cellChildMap[row, col] = data[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone
|
||||
// Implements the LayoutModel.Clone abstract method
|
||||
// Clones the data from this GridLayoutModel to a new GridLayoutModel
|
||||
public override LayoutModel Clone()
|
||||
{
|
||||
GridLayoutModel layout = new GridLayoutModel(Name);
|
||||
int rows = Rows;
|
||||
int cols = Columns;
|
||||
|
||||
layout.Rows = rows;
|
||||
layout.Columns = cols;
|
||||
|
||||
int[,] cellChildMap = new int[rows, cols];
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
cellChildMap[row, col] = CellChildMap[row, col];
|
||||
}
|
||||
}
|
||||
layout.CellChildMap = cellChildMap;
|
||||
|
||||
int[] rowPercents = new int[rows];
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
rowPercents[row] = RowPercents[row];
|
||||
}
|
||||
layout.RowPercents = rowPercents;
|
||||
|
||||
int[] colPercents = new int[cols];
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
colPercents[col] = ColumnPercents[col];
|
||||
}
|
||||
layout.ColumnPercents = colPercents;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
// GetPersistData
|
||||
// Implements the LayoutModel.GetPersistData abstract method
|
||||
// Returns the state of this GridLayoutModel in persisted format
|
||||
protected override byte[] GetPersistData()
|
||||
{
|
||||
int rows = Rows;
|
||||
int cols = Columns;
|
||||
|
||||
int[,] cellChildMap;
|
||||
|
||||
if (_freeZones.Count == 0)
|
||||
{
|
||||
// no unused indices -- so we can just use the _cellChildMap as is
|
||||
cellChildMap = _cellChildMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
// compress cellChildMap to not have gaps for unused child indices;
|
||||
List<int> mapping = new List<int>();
|
||||
|
||||
cellChildMap = new int[rows, cols];
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
int source = _cellChildMap[row, col];
|
||||
|
||||
int index = mapping.IndexOf(source);
|
||||
if (index == -1)
|
||||
{
|
||||
index = mapping.Count;
|
||||
mapping.Add(source);
|
||||
}
|
||||
cellChildMap[row, col] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] data = new byte[7 + (Rows * 2) + (Columns * 2) + (Rows * Columns)];
|
||||
|
||||
int i = 0;
|
||||
// Common persisted values between all layout types
|
||||
data[i++] = (byte)(c_latestVersion / 256);
|
||||
data[i++] = (byte)(c_latestVersion % 256);
|
||||
data[i++] = 0; // LayoutModelType: 0 == GridLayoutModel
|
||||
data[i++] = (byte)(Id / 256);
|
||||
data[i++] = (byte)(Id % 256);
|
||||
// End common
|
||||
|
||||
data[i++] = (byte)Rows;
|
||||
data[i++] = (byte)Columns;
|
||||
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
int rowPercent = _rowPercents[row];
|
||||
data[i++] = (byte)(rowPercent / 256);
|
||||
data[i++] = (byte)(rowPercent % 256);
|
||||
}
|
||||
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
int colPercent = _colPercents[col];
|
||||
data[i++] = (byte)(colPercent / 256);
|
||||
data[i++] = (byte)(colPercent % 256);
|
||||
}
|
||||
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
data[i++] = (byte)cellChildMap[row, col];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static ushort c_latestVersion = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace FancyZonesEditor.Models
|
||||
{
|
||||
// Base LayoutModel
|
||||
// Manages common properties and base persistence
|
||||
public abstract class LayoutModel : INotifyPropertyChanged
|
||||
{
|
||||
protected LayoutModel() { }
|
||||
|
||||
protected LayoutModel(string name) : this()
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
protected LayoutModel(string name, ushort id) : this(name)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
// Name - the display name for this layout model - is also used as the key in the registry
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
{
|
||||
if (_name != value)
|
||||
{
|
||||
_name = value;
|
||||
FirePropertyChanged("Name");
|
||||
}
|
||||
}
|
||||
}
|
||||
private string _name;
|
||||
|
||||
// Id - the unique ID for this layout model - is used to connect fancy zones' ZonesSets with the editor's Layouts
|
||||
// - note: 0 means this is a new layout, which means it will have its ID auto-assigned on persist
|
||||
public ushort Id
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_id == 0)
|
||||
{
|
||||
_id = ++s_maxId;
|
||||
}
|
||||
return _id;
|
||||
}
|
||||
}
|
||||
private ushort _id = 0;
|
||||
|
||||
// IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker
|
||||
// TODO: once we switch to a picker per monitor, we need to move this state to the view
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return _isSelected; }
|
||||
set
|
||||
{
|
||||
if (_isSelected != value)
|
||||
{
|
||||
_isSelected = value;
|
||||
FirePropertyChanged("IsSelected");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _isSelected;
|
||||
|
||||
// implementation of INotifyProeprtyChanged
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
// FirePropertyChanged -- wrapper that calls INPC.PropertyChanged
|
||||
protected virtual void FirePropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChangedEventHandler handler = PropertyChanged;
|
||||
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
// Removes this Layout from the registry and the loaded CustomModels list
|
||||
public void Delete()
|
||||
{
|
||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(c_registryPath, true);
|
||||
if (key != null)
|
||||
{
|
||||
key.DeleteValue(Name);
|
||||
}
|
||||
|
||||
int i = s_customModels.IndexOf(this);
|
||||
if (i != -1)
|
||||
{
|
||||
s_customModels.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Loads all the Layouts persisted under the Layouts key in the registry
|
||||
public static ObservableCollection<LayoutModel> LoadCustomModels()
|
||||
{
|
||||
s_customModels = new ObservableCollection<LayoutModel>();
|
||||
|
||||
RegistryKey key = Registry.CurrentUser.OpenSubKey(c_registryPath);
|
||||
if (key != null)
|
||||
{
|
||||
foreach (string name in key.GetValueNames())
|
||||
{
|
||||
LayoutModel model = null;
|
||||
byte[] data = (byte[])Registry.GetValue(c_fullRegistryPath, name, null);
|
||||
|
||||
ushort version = (ushort) (data[0]*256 + data[1]);
|
||||
byte type = data[2];
|
||||
ushort id = (ushort) (data[3]*256 + data[4]);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0: model = new GridLayoutModel(version, name, id, data); break;
|
||||
case 1: model = new CanvasLayoutModel(version, name, id, data); break;
|
||||
}
|
||||
|
||||
if (model != null)
|
||||
{
|
||||
if (s_maxId < id)
|
||||
{
|
||||
s_maxId = id;
|
||||
}
|
||||
s_customModels.Add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_customModels;
|
||||
}
|
||||
private static ObservableCollection<LayoutModel> s_customModels = null;
|
||||
|
||||
private static ushort s_maxId = 0;
|
||||
|
||||
// Callbacks that the base LayoutModel makes to derived types
|
||||
protected abstract byte[] GetPersistData();
|
||||
public abstract LayoutModel Clone();
|
||||
|
||||
// PInvokes to handshake with fancyzones backend
|
||||
internal static class Native
|
||||
{
|
||||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
|
||||
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
|
||||
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
internal delegate int PersistZoneSet(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string activeKey,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string key,
|
||||
ushort layoutId,
|
||||
int zoneCount,
|
||||
[MarshalAs(UnmanagedType.LPArray)] int[] zoneArray);
|
||||
}
|
||||
|
||||
public void Persist(System.Windows.Int32Rect[] zones)
|
||||
{
|
||||
// Persist the editor data
|
||||
Registry.SetValue(c_fullRegistryPath, Name, GetPersistData(), Microsoft.Win32.RegistryValueKind.Binary);
|
||||
Apply(zones);
|
||||
}
|
||||
|
||||
public void Apply(System.Windows.Int32Rect[] zones)
|
||||
{
|
||||
// Persist the zone data back into FZ
|
||||
var module = Native.LoadLibrary("fancyzones.dll");
|
||||
if (module == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pfn = Native.GetProcAddress(module, "PersistZoneSet");
|
||||
if (pfn == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Scale all the zones to the DPI and then pack them up to be marshalled.
|
||||
int zoneCount = zones.Length;
|
||||
var zoneArray = new int[zoneCount * 4];
|
||||
var graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
|
||||
float dpi = graphics.DpiX / 96;
|
||||
for (int i = 0; i < zones.Length; i++)
|
||||
{
|
||||
var left = (int)(zones[i].X * dpi);
|
||||
var top = (int)(zones[i].Y * dpi);
|
||||
var right = left + (int)(zones[i].Width * dpi);
|
||||
var bottom = top + (int)(zones[i].Height * dpi);
|
||||
|
||||
var index = i * 4;
|
||||
zoneArray[index] = left;
|
||||
zoneArray[index+1] = top;
|
||||
zoneArray[index+2] = right;
|
||||
zoneArray[index+3] = bottom;
|
||||
}
|
||||
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
if (args.Length > 1)
|
||||
{
|
||||
// args[1] = registry key value of currently active ZoneSet
|
||||
// args[2] = id of layout to load at startup
|
||||
|
||||
string uniqueId = args[1];
|
||||
|
||||
// TODO: multimon
|
||||
double height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
|
||||
double width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
|
||||
var key = width.ToString() + "_" + height.ToString();
|
||||
|
||||
var persistZoneSet = Marshal.GetDelegateForFunctionPointer<Native.PersistZoneSet>(pfn);
|
||||
persistZoneSet(uniqueId, key, _id, zoneCount, zoneArray);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string c_registryPath = Settings.RegistryPath + "\\Layouts";
|
||||
private static readonly string c_fullRegistryPath = Settings.FullRegistryPath + "\\Layouts";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using FancyZonesEditor.Models;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
//
|
||||
// Settings
|
||||
// These are the configuration settings used by the rest of the editor
|
||||
// Other UIs in the editor will subscribe to change events on the properties to stay up to date as these properties change
|
||||
//
|
||||
public class Settings : INotifyPropertyChanged
|
||||
{
|
||||
public Settings()
|
||||
{
|
||||
Rect workArea = System.Windows.SystemParameters.WorkArea;
|
||||
|
||||
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
|
||||
_defaultModels = new List<LayoutModel>(5);
|
||||
_focusModel = new CanvasLayoutModel("Focus", c_focusModelId, (int)workArea.Width, (int)workArea.Height);
|
||||
_defaultModels.Add(_focusModel);
|
||||
|
||||
_columnsModel = new GridLayoutModel("Columns", c_columnsModelId);
|
||||
_columnsModel.Rows = 1;
|
||||
_columnsModel.RowPercents = new int[1] { c_multiplier };
|
||||
_defaultModels.Add(_columnsModel);
|
||||
|
||||
_rowsModel = new GridLayoutModel("Rows", c_rowsModelId);
|
||||
_rowsModel.Columns = 1;
|
||||
_rowsModel.ColumnPercents = new int[1] { c_multiplier };
|
||||
_defaultModels.Add(_rowsModel);
|
||||
|
||||
_gridModel = new GridLayoutModel("Grid", c_gridModelId);
|
||||
_defaultModels.Add(_gridModel);
|
||||
|
||||
_priorityGridModel = new GridLayoutModel("Priority Grid", c_priorityGridModelId);
|
||||
_defaultModels.Add(_priorityGridModel);
|
||||
|
||||
_blankCustomModel = new CanvasLayoutModel("Create new custom", c_blankCustomModelId, (int)workArea.Width, (int)workArea.Height);
|
||||
|
||||
_zoneCount = (int)Registry.GetValue(FullRegistryPath, "ZoneCount", 3);
|
||||
_spacing = (int)Registry.GetValue(FullRegistryPath, "Spacing", 16);
|
||||
_showSpacing = (int)Registry.GetValue(FullRegistryPath, "ShowSpacing", 1) == 1;
|
||||
|
||||
UpdateLayoutModels();
|
||||
}
|
||||
|
||||
// ZoneCount - number of zones selected in the picker window
|
||||
public int ZoneCount
|
||||
{
|
||||
get { return _zoneCount; }
|
||||
set
|
||||
{
|
||||
if (_zoneCount != value)
|
||||
{
|
||||
_zoneCount = value;
|
||||
Registry.SetValue(FullRegistryPath, "ZoneCount", _zoneCount, RegistryValueKind.DWord);
|
||||
UpdateLayoutModels();
|
||||
FirePropertyChanged("ZoneCount");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _zoneCount;
|
||||
|
||||
// Spacing - how much space in between zones of the grid do you want
|
||||
public int Spacing
|
||||
{
|
||||
get { return _spacing; }
|
||||
set
|
||||
{
|
||||
if (_spacing != value)
|
||||
{
|
||||
_spacing = value;
|
||||
Registry.SetValue(FullRegistryPath, "Spacing", _spacing, RegistryValueKind.DWord);
|
||||
FirePropertyChanged("Spacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _spacing;
|
||||
|
||||
// ShowSpacing - is the Spacing value used or ignored?
|
||||
public bool ShowSpacing
|
||||
{
|
||||
get { return _showSpacing; }
|
||||
set
|
||||
{
|
||||
if (_showSpacing != value)
|
||||
{
|
||||
_showSpacing = value;
|
||||
Registry.SetValue(FullRegistryPath, "ShowSpacing", _showSpacing, RegistryValueKind.DWord);
|
||||
FirePropertyChanged("ShowSpacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _showSpacing;
|
||||
|
||||
// IsShiftKeyPressed - is the shift key currently being held down
|
||||
public bool IsShiftKeyPressed
|
||||
{
|
||||
get { return _isShiftKeyPressed; }
|
||||
set
|
||||
{
|
||||
if (_isShiftKeyPressed != value)
|
||||
{
|
||||
_isShiftKeyPressed = value;
|
||||
FirePropertyChanged("IsShiftKeyPressed");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _isShiftKeyPressed;
|
||||
|
||||
// IsCtrlKeyPressed - is the ctrl key currently being held down
|
||||
public bool IsCtrlKeyPressed
|
||||
{
|
||||
get { return _isCtrlKeyPressed; }
|
||||
set
|
||||
{
|
||||
if (_isCtrlKeyPressed != value)
|
||||
{
|
||||
_isCtrlKeyPressed = value;
|
||||
FirePropertyChanged("IsCtrlKeyPressed");
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _isCtrlKeyPressed;
|
||||
|
||||
// UpdateLayoutModels
|
||||
// Update the five default layouts based on the new ZoneCount
|
||||
private void UpdateLayoutModels()
|
||||
{
|
||||
int previousZoneCount = _focusModel.Zones.Count;
|
||||
|
||||
// Update the "Focus" Default Layout
|
||||
_focusModel.Zones.Clear();
|
||||
|
||||
Int32Rect focusZoneRect = new Int32Rect((int)(_focusModel.ReferenceWidth * 0.1), (int)(_focusModel.ReferenceHeight * 0.1), (int)(_focusModel.ReferenceWidth * 0.6), (int)(_focusModel.ReferenceHeight * 0.6));
|
||||
int focusRectXIncrement = (ZoneCount <= 1) ? 0 : (int)(_focusModel.ReferenceWidth * 0.2) / (ZoneCount - 1);
|
||||
int focusRectYIncrement = (ZoneCount <= 1) ? 0 : (int)(_focusModel.ReferenceHeight * 0.2) / (ZoneCount - 1);
|
||||
|
||||
for (int i = 0; i < ZoneCount; i++)
|
||||
{
|
||||
_focusModel.Zones.Add(focusZoneRect);
|
||||
focusZoneRect.X += focusRectXIncrement;
|
||||
focusZoneRect.Y += focusRectYIncrement;
|
||||
}
|
||||
|
||||
// Update the "Rows" and "Columns" Default Layouts
|
||||
// They can share their model, just transposed
|
||||
_rowsModel.CellChildMap = new int[ZoneCount, 1];
|
||||
_columnsModel.CellChildMap = new int[1, ZoneCount];
|
||||
_rowsModel.Rows = _columnsModel.Columns = ZoneCount;
|
||||
_rowsModel.RowPercents = _columnsModel.ColumnPercents = new int[ZoneCount];
|
||||
|
||||
for (int i = 0; i < ZoneCount; i++)
|
||||
{
|
||||
_rowsModel.CellChildMap[i, 0] = i;
|
||||
_columnsModel.CellChildMap[0, i] = i;
|
||||
_rowsModel.RowPercents[i] = c_multiplier / ZoneCount; // _columnsModel is sharing the same array
|
||||
}
|
||||
|
||||
// Update the "Grid" Default Layout
|
||||
int rows = 1;
|
||||
int cols = 1;
|
||||
int mergeCount = 0;
|
||||
while (ZoneCount / rows >= rows)
|
||||
{
|
||||
rows++;
|
||||
}
|
||||
rows--;
|
||||
cols = ZoneCount / rows;
|
||||
if (ZoneCount % rows == 0)
|
||||
{
|
||||
// even grid
|
||||
}
|
||||
else
|
||||
{
|
||||
cols++;
|
||||
mergeCount = rows - (ZoneCount % rows);
|
||||
}
|
||||
_gridModel.Rows = rows;
|
||||
_gridModel.Columns = cols;
|
||||
_gridModel.RowPercents = new int[rows];
|
||||
_gridModel.ColumnPercents = new int[cols];
|
||||
_gridModel.CellChildMap = new int[rows, cols];
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
_gridModel.RowPercents[row] = c_multiplier / rows;
|
||||
}
|
||||
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
_gridModel.ColumnPercents[col] = c_multiplier / cols;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int col = cols - 1; col >= 0; col--)
|
||||
{
|
||||
for (int row = rows - 1; row >= 0; row--)
|
||||
{
|
||||
_gridModel.CellChildMap[row, col] = index++;
|
||||
if (index == ZoneCount)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Update the "Priority Grid" Default Layout
|
||||
if (ZoneCount <= s_priorityData.Length)
|
||||
{
|
||||
_priorityGridModel.Reload(s_priorityData[ZoneCount - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// same as grid;
|
||||
_priorityGridModel.Rows = _gridModel.Rows;
|
||||
_priorityGridModel.Columns = _gridModel.Columns;
|
||||
_priorityGridModel.RowPercents = _gridModel.RowPercents;
|
||||
_priorityGridModel.ColumnPercents = _gridModel.ColumnPercents;
|
||||
_priorityGridModel.CellChildMap = _gridModel.CellChildMap;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<LayoutModel> DefaultModels { get { return _defaultModels; } }
|
||||
public ObservableCollection<LayoutModel> CustomModels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_customModels == null)
|
||||
{
|
||||
_customModels = LayoutModel.LoadCustomModels();
|
||||
_customModels.Insert(0, _blankCustomModel);
|
||||
}
|
||||
return _customModels;
|
||||
}
|
||||
}
|
||||
private ObservableCollection<LayoutModel> _customModels;
|
||||
|
||||
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
|
||||
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
|
||||
|
||||
public static bool IsPredefinedLayout(LayoutModel model)
|
||||
{
|
||||
return (model.Id >= c_lastPrefinedId);
|
||||
}
|
||||
|
||||
// implementation of INotifyProeprtyChanged
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
// FirePropertyChanged -- wrapper that calls INPC.PropertyChanged
|
||||
protected virtual void FirePropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChangedEventHandler handler = PropertyChanged;
|
||||
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
// storage for Default Layout Models
|
||||
private IList<LayoutModel> _defaultModels;
|
||||
private CanvasLayoutModel _focusModel;
|
||||
private GridLayoutModel _rowsModel;
|
||||
private GridLayoutModel _columnsModel;
|
||||
private GridLayoutModel _gridModel;
|
||||
private GridLayoutModel _priorityGridModel;
|
||||
private CanvasLayoutModel _blankCustomModel;
|
||||
|
||||
private static readonly ushort c_focusModelId = 0xFFFF;
|
||||
private static readonly ushort c_rowsModelId = 0xFFFE;
|
||||
private static readonly ushort c_columnsModelId = 0xFFFD;
|
||||
private static readonly ushort c_gridModelId = 0xFFFC;
|
||||
private static readonly ushort c_priorityGridModelId = 0xFFFB;
|
||||
private static readonly ushort c_blankCustomModelId = 0xFFFA;
|
||||
private static readonly ushort c_lastPrefinedId = c_blankCustomModelId;
|
||||
|
||||
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
|
||||
private static byte[][] s_priorityData = new byte[][]
|
||||
{
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 },
|
||||
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 }
|
||||
};
|
||||
|
||||
private const int c_multiplier = 10000;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user