[FancyZones Editor] Quick layout switch hotkeys (#10437)

Co-authored-by: Ivan Stošić <ivan100sic@gmail.com>
This commit is contained in:
Seraphima Zykova
2021-03-25 15:44:55 +03:00
committed by GitHub
parent 7ba03ed24f
commit 13c4c188fa
34 changed files with 986 additions and 376 deletions

View File

@@ -284,6 +284,7 @@
</Grid>
</ScrollViewer>
<Button x:Name="NewLayoutButton"
Click="NewLayoutButton_Click"
HorizontalAlignment="Right"
@@ -355,6 +356,7 @@
PrimaryButtonClick="EditLayoutDialog_PrimaryButtonClick"
SecondaryButtonClick="EditLayoutDialog_SecondaryButtonClick"
Title="{x:Static props:Resources.Edit_Layout}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Opened="Dialog_Opened"
Closed="Dialog_Closed">
<Grid DataContext="{Binding SelectedModel}"
@@ -419,55 +421,55 @@
HorizontalAlignment="Center"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeTemplateToVisibilityConverter}}">
<Button x:Name="decrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}"
ToolTip="{x:Static props:Resources.Zone_Count_Decrement}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="DecrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE108;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
<TextBlock x:Name="zoneCount"
Text="{Binding TemplateZoneCount}"
FontWeight="SemiBold"
FontSize="18"
Width="32"
HorizontalAlignment="Center"
TextAlignment="Center"
Margin="0,-4,0,0"
ToolTip="Number of zones"
VerticalAlignment="Center" />
<Button x:Name="decrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}"
ToolTip="{x:Static props:Resources.Zone_Count_Decrement}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="DecrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE108;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
<Button x:Name="incrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}"
ToolTip="{x:Static props:Resources.Zone_Count_Increment}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="IncrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE109;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
</StackPanel>
<TextBlock x:Name="zoneCount"
Text="{Binding TemplateZoneCount}"
FontWeight="SemiBold"
FontSize="18"
Width="32"
HorizontalAlignment="Center"
TextAlignment="Center"
Margin="0,-4,0,0"
ToolTip="Number of zones"
VerticalAlignment="Center" />
<Button x:Name="incrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}"
ToolTip="{x:Static props:Resources.Zone_Count_Increment}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="IncrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE109;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
</StackPanel>
<TextBox Text="{Binding Name}"
ui:ControlHelper.Header="{x:Static props:Resources.Name}"
Margin="0,16,0,0"
Margin="0,16,2,0"
Visibility="{Binding IsCustom, Converter={StaticResource BooleanToVisibilityConverter}}"
HorizontalAlignment="Stretch" />
<CheckBox x:Name="spaceAroundSetting"
Content="{x:Static props:Resources.Show_Space_Zones}"
IsChecked="{Binding ShowSpacing}"
Margin="0,16,0,0"
Margin="0,16,12,0"
Visibility="{Binding Converter={StaticResource LayoutModelTypeToVisibilityConverter}}" />
<ui:NumberBox Margin="0,6,0,0"
@@ -483,7 +485,8 @@
<TextBlock Text="{x:Static props:Resources.Distance_adjacent_zones}"
IsEnabled="{Binding ShowSpacing}"
Margin="0,16,0,0"
Margin="0,16,12,0"
TextWrapping="Wrap"
Foreground="{DynamicResource PrimaryForegroundBrush}"
x:Name="sensitivityRadiusValue" />
@@ -495,6 +498,18 @@
SpinButtonPlacementMode="Compact"
HorizontalAlignment="Left"
AutomationProperties.LabeledBy="{Binding ElementName=sensitivityRadiusValue}" />
<TextBlock Text="{x:Static props:Resources.QuickKey_Select}"
Margin="0,16,12,0"
Foreground="{DynamicResource PrimaryForegroundBrush}"
TextWrapping="Wrap"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}"/>
<ComboBox x:Name="quickKeySelectionComboBox"
Margin="0,6,0,0"
ItemsSource="{Binding QuickKeysAvailable}"
SelectedItem="{Binding QuickKey}"
Width="106"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}"/>
</StackPanel>
</Grid>
</ui:ContentDialog>

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
@@ -16,6 +17,8 @@ namespace FancyZonesEditor.Models
{
_guid = Guid.NewGuid();
Type = LayoutType.Custom;
MainWindowSettingsModel.QuickKeys.PropertyChanged += QuickSwitchKeys_PropertyChanged;
}
protected LayoutModel(string name)
@@ -48,6 +51,9 @@ namespace FancyZonesEditor.Models
_isApplied = other._isApplied;
_sensitivityRadius = other._sensitivityRadius;
_zoneCount = other._zoneCount;
_quickKey = other._quickKey;
MainWindowSettingsModel.QuickKeys.PropertyChanged += QuickSwitchKeys_PropertyChanged;
}
// Name - the display name for this layout model - is also used as the key in the registry
@@ -158,6 +164,55 @@ namespace FancyZonesEditor.Models
private int _sensitivityRadius = LayoutSettings.DefaultSensitivityRadius;
public List<string> QuickKeysAvailable
{
get
{
List<string> result = new List<string>();
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value == string.Empty || pair.Value == Uuid)
{
result.Add(pair.Key);
}
}
return result;
}
}
public string QuickKey
{
get
{
return _quickKey == -1 ? Properties.Resources.Quick_Key_None : _quickKey.ToString();
}
set
{
string none = Properties.Resources.Quick_Key_None;
var intValue = value == none ? -1 : int.Parse(value);
if (intValue != _quickKey)
{
string prev = _quickKey == -1 ? none : _quickKey.ToString();
_quickKey = intValue;
if (intValue != -1)
{
MainWindowSettingsModel.QuickKeys.SelectKey(value, Uuid);
}
else
{
MainWindowSettingsModel.QuickKeys.FreeKey(prev);
}
FirePropertyChanged(nameof(QuickKey));
}
}
}
private int _quickKey = -1;
// TemplateZoneCount - number of zones selected in the picker window for template layouts
public int TemplateZoneCount
{
@@ -200,6 +255,11 @@ namespace FancyZonesEditor.Models
// Removes this Layout from the registry and the loaded CustomModels list
public void Delete()
{
if (_quickKey != -1)
{
MainWindowSettingsModel.QuickKeys.FreeKey(QuickKey);
}
var customModels = MainWindowSettingsModel.CustomModels;
int i = customModels.IndexOf(this);
if (i != -1)
@@ -241,5 +301,17 @@ namespace FancyZonesEditor.Models
{
PersistData();
}
private void QuickSwitchKeys_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value == Uuid)
{
QuickKey = pair.Key.ToString();
break;
}
}
}
}
}

View File

@@ -6,7 +6,7 @@ namespace FancyZonesEditor.Models
{
public enum LayoutType
{
Blank = -1,
Blank = 0,
Focus,
Columns,
Rows,

View File

@@ -23,13 +23,6 @@ namespace FancyZonesEditor
VirtualDesktopId,
}
private readonly CanvasLayoutModel _blankModel;
private readonly CanvasLayoutModel _focusModel;
private readonly GridLayoutModel _rowsModel;
private readonly GridLayoutModel _columnsModel;
private readonly GridLayoutModel _gridModel;
private readonly GridLayoutModel _priorityGridModel;
// Non-localizable strings
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
@@ -53,40 +46,40 @@ namespace FancyZonesEditor
public MainWindowSettingsModel()
{
// Initialize default layout models: Blank, Focus, Columns, Rows, Grid, and PriorityGrid
_blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank)
var blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank)
{
TemplateZoneCount = 0,
SensitivityRadius = 0,
};
DefaultModels.Add(_blankModel);
DefaultModels.Insert((int)LayoutType.Blank, blankModel);
_focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
_focusModel.InitTemplateZones();
DefaultModels.Add(_focusModel);
var focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
focusModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Focus, focusModel);
_columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
var columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
{
Rows = 1,
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_columnsModel.InitTemplateZones();
DefaultModels.Add(_columnsModel);
columnsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Columns, columnsModel);
_rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
var rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
{
Columns = 1,
ColumnPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_rowsModel.InitTemplateZones();
DefaultModels.Add(_rowsModel);
rowsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Rows, rowsModel);
_gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
_gridModel.InitTemplateZones();
DefaultModels.Add(_gridModel);
var gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
gridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Grid, gridModel);
_priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
_priorityGridModel.InitTemplateZones();
DefaultModels.Add(_priorityGridModel);
var priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
priorityGridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.PriorityGrid, priorityGridModel);
}
// IsShiftKeyPressed - is the shift key currently being held down
@@ -133,7 +126,7 @@ namespace FancyZonesEditor
{
get
{
return _blankModel;
return DefaultModels[(int)LayoutType.Blank];
}
}
@@ -149,6 +142,8 @@ namespace FancyZonesEditor
private static ObservableCollection<LayoutModel> _customModels = new ObservableCollection<LayoutModel>();
public static QuickKeysModel QuickKeys { get; } = new QuickKeysModel();
public LayoutModel SelectedModel
{
get
@@ -202,7 +197,7 @@ namespace FancyZonesEditor
{
foreach (LayoutModel model in CustomModels)
{
if ("{" + model.Guid.ToString().ToUpperInvariant() + "}" == currentApplied.ZonesetUuid.ToUpperInvariant())
if (model.Uuid == currentApplied.ZonesetUuid.ToUpperInvariant())
{
// found match
foundModel = model;
@@ -234,7 +229,7 @@ namespace FancyZonesEditor
if (foundModel == null)
{
foundModel = _priorityGridModel;
foundModel = DefaultModels[(int)LayoutType.PriorityGrid];
}
SetSelectedModel(foundModel);
@@ -255,6 +250,7 @@ namespace FancyZonesEditor
SelectedModel.IsSelected = model.IsSelected;
SelectedModel.IsApplied = model.IsApplied;
SelectedModel.Name = model.Name;
SelectedModel.QuickKey = model.QuickKey;
if (model is GridLayoutModel grid)
{

View File

@@ -0,0 +1,87 @@
// 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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace FancyZonesEditor.Models
{
public class QuickKeysModel : INotifyPropertyChanged
{
public SortedDictionary<string, string> SelectedKeys { get; } = new SortedDictionary<string, string>()
{
{ Properties.Resources.Quick_Key_None, string.Empty },
{ "0", string.Empty },
{ "1", string.Empty },
{ "2", string.Empty },
{ "3", string.Empty },
{ "4", string.Empty },
{ "5", string.Empty },
{ "6", string.Empty },
{ "7", string.Empty },
{ "8", string.Empty },
{ "9", string.Empty },
};
public QuickKeysModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void FreeKey(string key)
{
if (SelectedKeys.ContainsKey(key))
{
SelectedKeys[key] = string.Empty;
FirePropertyChanged();
}
}
public bool SelectKey(string key, string uuid)
{
if (!SelectedKeys.ContainsKey(key))
{
return false;
}
if (SelectedKeys[key] == uuid)
{
return true;
}
// clean previous value
foreach (var pair in SelectedKeys)
{
if (pair.Value == uuid)
{
SelectedKeys[pair.Key] = string.Empty;
break;
}
}
SelectedKeys[key] = uuid;
FirePropertyChanged();
return true;
}
public void CleanUp()
{
var keys = SelectedKeys.Keys.ToList();
foreach (var key in keys)
{
SelectedKeys[key] = string.Empty;
}
FirePropertyChanged();
}
protected virtual void FirePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -555,6 +555,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to None.
/// </summary>
public static string Quick_Key_None {
get {
return ResourceManager.GetString("Quick_Key_None", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select a key to quickly apply the layout (Win + Ctrl + Alt + key).
/// </summary>
public static string QuickKey_Select {
get {
return ResourceManager.GetString("QuickKey_Select", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reset layout.
/// </summary>

View File

@@ -346,4 +346,10 @@
<value>Splitter:</value>
<comment>Title for concept: A segmenter visual for splitting one item into two. This would be the vertical line</comment>
</data>
<data name="QuickKey_Select" xml:space="preserve">
<value>Select a key to quickly apply the layout (Win + Ctrl + Alt + key)</value>
</data>
<data name="Quick_Key_None" xml:space="preserve">
<value>None</value>
</data>
</root>

View File

@@ -57,6 +57,7 @@ namespace FancyZonesEditor.Utils
MonitorTop,
}
// parsing cmd args
private struct NativeMonitorData
{
public string MonitorId { get; set; }
@@ -87,6 +88,7 @@ namespace FancyZonesEditor.Utils
}
}
// zones-settings: devices
private struct DeviceWrapper
{
public struct ActiveZoneSetWrapper
@@ -109,6 +111,7 @@ namespace FancyZonesEditor.Utils
public int EditorSensitivityRadius { get; set; }
}
// zones-settings: custom-zone-sets
private class CanvasInfoWrapper
{
public struct CanvasZoneWrapper
@@ -131,6 +134,7 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius;
}
// zones-settings: custom-zone-sets
private class GridInfoWrapper
{
public int Rows { get; set; }
@@ -150,6 +154,7 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius;
}
// zones-settings: custom-zone-sets
private struct CustomLayoutWrapper
{
public string Uuid { get; set; }
@@ -161,6 +166,7 @@ namespace FancyZonesEditor.Utils
public JsonElement Info { get; set; } // CanvasInfoWrapper or GridInfoWrapper
}
// zones-settings: templates
private struct TemplateLayoutWrapper
{
public string Type { get; set; }
@@ -174,6 +180,15 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; }
}
// zones-settings: quick-layout-keys-wrapper
private struct QuickLayoutKeysWrapper
{
public int Key { get; set; }
public string Uuid { get; set; }
}
// zones-settings
private struct ZoneSettingsWrapper
{
public List<DeviceWrapper> Devices { get; set; }
@@ -181,6 +196,8 @@ namespace FancyZonesEditor.Utils
public List<CustomLayoutWrapper> CustomZoneSets { get; set; }
public List<TemplateLayoutWrapper> Templates { get; set; }
public List<QuickLayoutKeysWrapper> QuickLayoutKeys { get; set; }
}
private struct EditorParams
@@ -528,6 +545,7 @@ namespace FancyZonesEditor.Utils
bool devicesParsingResult = SetDevices(zoneSettings.Devices);
bool customZonesParsingResult = SetCustomLayouts(zoneSettings.CustomZoneSets);
bool templatesParsingResult = SetTemplateLayouts(zoneSettings.Templates);
bool quickLayoutSwitchKeysParsingResult = SetQuickLayoutSwitchKeys(zoneSettings.QuickLayoutKeys);
if (!devicesParsingResult || !customZonesParsingResult)
{
@@ -549,6 +567,7 @@ namespace FancyZonesEditor.Utils
zoneSettings.Devices = new List<DeviceWrapper>();
zoneSettings.CustomZoneSets = new List<CustomLayoutWrapper>();
zoneSettings.Templates = new List<TemplateLayoutWrapper>();
zoneSettings.QuickLayoutKeys = new List<QuickLayoutKeysWrapper>();
// Serialize used devices
foreach (var monitor in App.Overlay.Monitors)
@@ -685,6 +704,27 @@ namespace FancyZonesEditor.Utils
zoneSettings.Templates.Add(wrapper);
}
// Serialize quick layout switch keys
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value != string.Empty)
{
try
{
QuickLayoutKeysWrapper wrapper = new QuickLayoutKeysWrapper
{
Key = int.Parse(pair.Key),
Uuid = pair.Value,
};
zoneSettings.QuickLayoutKeys.Add(wrapper);
}
catch (Exception)
{
}
}
}
try
{
string jsonString = JsonSerializer.Serialize(zoneSettings, _options);
@@ -781,7 +821,15 @@ namespace FancyZonesEditor.Utils
zones.Add(new Int32Rect { X = (int)zone.X, Y = (int)zone.Y, Width = (int)zone.Width, Height = (int)zone.Height });
}
layout = new CanvasLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, zones, info.RefWidth, info.RefHeight);
try
{
layout = new CanvasLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, zones, info.RefWidth, info.RefHeight);
}
catch (Exception)
{
continue;
}
layout.SensitivityRadius = info.SensitivityRadius;
}
else if (zoneSet.Type == GridLayoutModel.ModelTypeID)
@@ -797,7 +845,15 @@ namespace FancyZonesEditor.Utils
}
}
layout = new GridLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, info.Rows, info.Columns, info.RowsPercentage, info.ColumnsPercentage, cells);
try
{
layout = new GridLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, info.Rows, info.Columns, info.RowsPercentage, info.ColumnsPercentage, cells);
}
catch (Exception)
{
continue;
}
layout.SensitivityRadius = info.SensitivityRadius;
(layout as GridLayoutModel).ShowSpacing = info.ShowSpacing;
(layout as GridLayoutModel).Spacing = info.Spacing;
@@ -823,24 +879,35 @@ namespace FancyZonesEditor.Utils
foreach (var wrapper in templateLayouts)
{
var type = JsonTagToLayoutType(wrapper.Type);
LayoutType type = JsonTagToLayoutType(wrapper.Type);
LayoutModel layout = MainWindowSettingsModel.DefaultModels[(int)type];
foreach (var layout in MainWindowSettingsModel.DefaultModels)
layout.SensitivityRadius = wrapper.SensitivityRadius;
layout.TemplateZoneCount = wrapper.ZoneCount;
if (layout is GridLayoutModel grid)
{
if (layout.Type == type)
{
layout.SensitivityRadius = wrapper.SensitivityRadius;
layout.TemplateZoneCount = wrapper.ZoneCount;
if (layout is GridLayoutModel grid)
{
grid.ShowSpacing = wrapper.ShowSpacing;
grid.Spacing = wrapper.Spacing;
}
layout.InitTemplateZones();
}
grid.ShowSpacing = wrapper.ShowSpacing;
grid.Spacing = wrapper.Spacing;
}
layout.InitTemplateZones();
}
return true;
}
private bool SetQuickLayoutSwitchKeys(List<QuickLayoutKeysWrapper> quickSwitchKeys)
{
if (quickSwitchKeys == null)
{
return false;
}
MainWindowSettingsModel.QuickKeys.CleanUp();
foreach (var wrapper in quickSwitchKeys)
{
MainWindowSettingsModel.QuickKeys.SelectKey(wrapper.Key.ToString(), wrapper.Uuid);
}
return true;