diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index ac50aa86bd..04a6cac6c9 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -1456,6 +1456,7 @@ NDEBUG
ndp
neq
NESW
+netcore
netcoreapp
netframework
netfx
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor.sln b/src/modules/fancyzones/editor/FancyZonesEditor.sln
index 421bb54260..fe4d33c9f7 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor.sln
+++ b/src/modules/fancyzones/editor/FancyZonesEditor.sln
@@ -13,8 +13,8 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.Build.0 = Debug|x64
- {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.ActiveCfg = Debug|x64
- {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.ActiveCfg = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.config b/src/modules/fancyzones/editor/FancyZonesEditor/App.config
index de277144ee..c34d9ed0d9 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/App.config
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.config
@@ -3,4 +3,7 @@
+
+
+
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs
index 751fc6c40a..e7486c401a 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs
@@ -1,18 +1,17 @@
-// Copyright (c) Microsoft Corporation
+// 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.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
-using System.Runtime.InteropServices;
using System.Text;
-using System.Threading.Tasks;
using System.Windows;
-using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
using ManagedCommon;
namespace FancyZonesEditor
@@ -24,6 +23,7 @@ namespace FancyZonesEditor
{
// Non-localizable strings
private const string CrashReportLogFile = "FZEditorCrashLog.txt";
+ private const string ErrorReportLogFile = "FZEditorErrorLog.txt";
private const string PowerToysIssuesURL = "https://aka.ms/powerToysReportBug";
private const string CrashReportExceptionTag = "Exception";
@@ -44,57 +44,76 @@ namespace FancyZonesEditor
private readonly IFileSystem _fileSystem = new FileSystem();
- public Settings ZoneSettings { get; }
+ public MainWindowSettingsModel MainWindowSettings { get; }
+
+ public static FancyZonesEditorIO FancyZonesEditorIO { get; private set; }
+
+ public static Overlay Overlay { get; private set; }
+
+ public static int PowerToysPID { get; set; }
+
+ public static bool DebugMode
+ {
+ get
+ {
+ return _debugMode;
+ }
+ }
+
+ private static bool _debugMode = false;
+
+ [Conditional("DEBUG")]
+ private void DebugModeCheck()
+ {
+ _debugMode = true;
+ }
public App()
{
- ZoneSettings = new Settings();
+ DebugModeCheck();
+ FancyZonesEditorIO = new FancyZonesEditorIO();
+ Overlay = new Overlay();
+ MainWindowSettings = new MainWindowSettingsModel();
}
private void OnStartup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
- RunnerHelper.WaitForPowerToysRunner(Settings.PowerToysPID, () =>
+ RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Environment.Exit(0);
});
- LayoutModel foundModel = null;
+ FancyZonesEditorIO.ParseCommandLineArguments();
+ FancyZonesEditorIO.ParseDeviceInfoData();
- foreach (LayoutModel model in ZoneSettings.DefaultModels)
+ MainWindowSettingsModel settings = ((App)Current).MainWindowSettings;
+ settings.UpdateSelectedLayoutModel();
+
+ Overlay.Show();
+ }
+
+ public static void ShowExceptionMessageBox(string message, Exception exception = null)
+ {
+ string fullMessage = FancyZonesEditor.Properties.Resources.Error_Report + PowerToysIssuesURL + " \n" + message;
+ if (exception != null)
{
- if (model.Type == Settings.ActiveZoneSetLayoutType)
- {
- // found match
- foundModel = model;
- break;
- }
+ fullMessage += ": " + exception.Message;
}
- if (foundModel == null)
- {
- foreach (LayoutModel model in Settings.CustomModels)
- {
- if ("{" + model.Guid.ToString().ToUpper() + "}" == Settings.ActiveZoneSetUUid.ToUpper())
- {
- // found match
- foundModel = model;
- break;
- }
- }
- }
+ MessageBox.Show(fullMessage, FancyZonesEditor.Properties.Resources.Error_Exception_Message_Box_Title);
+ }
- if (foundModel == null)
- {
- foundModel = ZoneSettings.DefaultModels[0];
- }
+ public static void ShowExceptionReportMessageBox(string reportData)
+ {
+ var fileStream = File.OpenWrite(ErrorReportLogFile);
+ var sw = new StreamWriter(fileStream);
+ sw.Write(reportData);
+ sw.Flush();
+ fileStream.Close();
- foundModel.IsSelected = true;
-
- EditorOverlay overlay = new EditorOverlay();
- overlay.Show();
- overlay.DataContext = foundModel;
+ ShowReportMessageBox(fileStream.Name);
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
@@ -103,16 +122,22 @@ namespace FancyZonesEditor
var sw = new StreamWriter(fileStream);
sw.Write(FormatException((Exception)args.ExceptionObject));
fileStream.Close();
+
+ ShowReportMessageBox(fileStream.Name);
+ }
+
+ private static void ShowReportMessageBox(string fileName)
+ {
MessageBox.Show(
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part1 +
- Path.GetFullPath(fileStream.Name) +
+ Path.GetFullPath(fileName) +
"\n" +
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part2 +
PowerToysIssuesURL,
FancyZonesEditor.Properties.Resources.Fancy_Zones_Editor_App_Title);
}
- private string FormatException(Exception ex)
+ private static string FormatException(Exception ex)
{
var sb = new StringBuilder();
sb.AppendLine();
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml
index 27321f82cc..a1e82be18e 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml
@@ -5,7 +5,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
-
-
+
+
+
+
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs
index 76bf222c55..767b56f811 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs
@@ -1,10 +1,11 @@
-// Copyright (c) Microsoft Corporation
+// 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.Windows;
using System.Windows.Controls;
using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@@ -46,6 +47,10 @@ namespace FancyZonesEditor
private void UpdateZoneRects()
{
+ var workArea = App.Overlay.WorkArea;
+ Preview.Width = workArea.Width;
+ Preview.Height = workArea.Height;
+
UIElementCollection previewChildren = Preview.Children;
int previewChildrenCount = previewChildren.Count;
while (previewChildrenCount < _model.Zones.Count)
@@ -54,6 +59,7 @@ namespace FancyZonesEditor
{
Model = _model,
};
+
Preview.Children.Add(zone);
previewChildrenCount++;
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml
index 3650a644f6..a7eb95c12e 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml
@@ -13,7 +13,7 @@
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
- WindowStartupLocation="CenterScreen"
+ WindowStartupLocation="CenterOwner"
Closed="OnClosed">
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs
index 356cb7b177..994f1a54d4 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// 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.
@@ -19,24 +19,28 @@ namespace FancyZonesEditor
KeyUp += CanvasEditorWindow_KeyUp;
- _model = EditorOverlay.Current.DataContext as CanvasLayoutModel;
+ _model = App.Overlay.CurrentDataContext as CanvasLayoutModel;
_stashedModel = (CanvasLayoutModel)_model.Clone();
}
private void OnAddZone(object sender, RoutedEventArgs e)
{
- if (_offset + (int)(Settings.WorkArea.Width * 0.4) < (int)Settings.WorkArea.Width
- && _offset + (int)(Settings.WorkArea.Height * 0.4) < (int)Settings.WorkArea.Height)
+ Rect workingArea = App.Overlay.WorkArea;
+ int offset = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(_offset);
+
+ if (offset + (int)(workingArea.Width * 0.4) < (int)workingArea.Width
+ && offset + (int)(workingArea.Height * 0.4) < (int)workingArea.Height)
{
- _model.AddZone(new Int32Rect(_offset, _offset, (int)(Settings.WorkArea.Width * 0.4), (int)(Settings.WorkArea.Height * 0.4)));
+ _model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * 0.4), (int)(workingArea.Height * 0.4)));
}
else
{
_offset = 100;
- _model.AddZone(new Int32Rect(_offset, _offset, (int)(Settings.WorkArea.Width * 0.4), (int)(Settings.WorkArea.Height * 0.4)));
+ offset = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(_offset);
+ _model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * 0.4), (int)(workingArea.Height * 0.4)));
}
- _offset += 50;
+ _offset += 50; // TODO: replace hardcoded numbers
}
protected new void OnCancel(object sender, RoutedEventArgs e)
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
index 91c892217a..2678ccd077 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs
@@ -8,6 +8,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@@ -84,7 +85,7 @@ namespace FancyZonesEditor
}
}
- foreach (Rect singleMonitor in Settings.UsedWorkAreas)
+ foreach (Rect singleMonitor in App.Overlay.WorkAreas)
{
int monitorPositionLow = (int)(isX ? singleMonitor.Left : singleMonitor.Top);
int monitorPositionHigh = (int)(isX ? singleMonitor.Right : singleMonitor.Bottom);
@@ -213,8 +214,9 @@ namespace FancyZonesEditor
private SnappyHelperBase NewDefaultSnappyHelper(bool isX, ResizeMode mode)
{
- int screenAxisOrigin = (int)(isX ? Settings.WorkArea.Left : Settings.WorkArea.Top);
- int screenAxisSize = (int)(isX ? Settings.WorkArea.Width : Settings.WorkArea.Height);
+ Rect workingArea = App.Overlay.WorkArea;
+ int screenAxisOrigin = (int)(isX ? workingArea.Left : workingArea.Top);
+ int screenAxisSize = (int)(isX ? workingArea.Width : workingArea.Height);
return new SnappyHelperMagnetic(Model.Zones, ZoneIndex, isX, mode, screenAxisOrigin, screenAxisSize);
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Converters/ModelToVisibilityConverter.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Converters/ModelToVisibilityConverter.xaml.cs
index fee4cfb5b9..4d01967384 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Converters/ModelToVisibilityConverter.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Converters/ModelToVisibilityConverter.xaml.cs
@@ -13,7 +13,7 @@ namespace FancyZonesEditor.Converters
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
- return Settings.IsPredefinedLayout((LayoutModel)value) ? Visibility.Collapsed : Visibility.Visible;
+ return MainWindowSettingsModel.IsPredefinedLayout((LayoutModel)value) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs
deleted file mode 100644
index 008ffec2b8..0000000000
--- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml.cs
+++ /dev/null
@@ -1,137 +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.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using FancyZonesEditor.Models;
-
-namespace FancyZonesEditor
-{
- ///
- /// Interaction logic for EditorOverlay.xaml
- ///
- public partial class EditorOverlay : Window
- {
- public static EditorOverlay Current { get; set; }
-
- private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
- private LayoutPreview _layoutPreview;
-
- private UserControl _editor;
-
- private static MainWindow _mainWindow = new MainWindow();
-
- 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;
- }
-
- public EditorOverlay()
- {
- InitializeComponent();
- Current = this;
-
- Left = Settings.WorkArea.Left;
- Top = Settings.WorkArea.Top;
- Width = Settings.WorkArea.Width;
- Height = Settings.WorkArea.Height;
- }
-
- private void OnLoaded(object sender, RoutedEventArgs e)
- {
- ShowLayoutPicker();
- }
-
- public void ShowLayoutPicker()
- {
- _editor = null;
- _layoutPreview = new LayoutPreview
- {
- IsActualSize = true,
- Opacity = 0.5,
- };
-
- Content = _layoutPreview;
-
- _mainWindow.Owner = this;
- _mainWindow.ShowActivated = true;
- _mainWindow.Topmost = true;
- _mainWindow.Show();
- _mainWindow.LeftWindowCommands = null;
- _mainWindow.RightWindowCommands = null;
-
- // window is set to topmost to make sure it shows on top of PowerToys settings page
- // we can reset topmost flag now
- _mainWindow.Topmost = false;
- }
-
- // These event handlers are used to track the current state of the Shift and Ctrl keys on the keyboard
- // They reflect that current state into properties on the Settings object, which the Zone view will listen to in editing mode
- protected override void OnPreviewKeyDown(KeyEventArgs e)
- {
- _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
- _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
- base.OnPreviewKeyDown(e);
- }
-
- protected override void OnPreviewKeyUp(KeyEventArgs e)
- {
- _settings.IsShiftKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
- _settings.IsCtrlKeyPressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
- base.OnPreviewKeyUp(e);
- }
-
- public void Edit()
- {
- _layoutPreview = null;
- if (DataContext is GridLayoutModel)
- {
- _editor = new GridEditor();
- }
- else if (DataContext is CanvasLayoutModel)
- {
- _editor = new CanvasEditor();
- }
-
- Content = _editor;
- }
- }
-}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs
index 7f1f6a7951..50cd457980 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs
@@ -13,8 +13,8 @@ namespace FancyZonesEditor
{
protected void OnSaveApplyTemplate(object sender, RoutedEventArgs e)
{
- EditorOverlay mainEditor = EditorOverlay.Current;
- if (mainEditor.DataContext is LayoutModel model)
+ var mainEditor = App.Overlay;
+ if (mainEditor.CurrentDataContext is LayoutModel model)
{
// If new custom Canvas layout is created (i.e. edited Blank layout),
// it's type needs to be updated
@@ -30,14 +30,14 @@ namespace FancyZonesEditor
_backToLayoutPicker = false;
Close();
- EditorOverlay.Current.Close();
+ mainEditor.CloseEditor();
}
protected void OnClosed(object sender, EventArgs e)
{
if (_backToLayoutPicker)
{
- EditorOverlay.Current.ShowLayoutPicker();
+ App.Overlay.CloseEditor();
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj
index 6bf62391a1..211435d465 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj
@@ -89,6 +89,9 @@
images\FancyZonesEditor.ico
+
+ app.manifest
+
@@ -118,9 +121,16 @@
GlobalSuppressions.cs
+
+
+ LayoutOverlayWindow.xaml
+
+
+
+
@@ -140,10 +150,7 @@
-
-
- EditorOverlay.xaml
-
+
@@ -151,6 +158,15 @@
GridEditorWindow.xaml
+
+
+
+
+
+
+
+
+
WindowLayout.xaml
@@ -175,6 +191,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -191,10 +211,6 @@
MainWindow.xaml
Code
-
- Designer
- MSBuild:Compile
-
Designer
MSBuild:Compile
@@ -235,6 +251,7 @@
Resources.Designer.cs
+
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -253,6 +270,9 @@
12.2.5
+
+ 6.6.30107
+
4.7.2
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs
index 9e61428498..9610b50b68 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml
index 14b57f53cc..09a039c116 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml
@@ -19,33 +19,35 @@
-
-
-
-
+
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs
index 9b71ede841..1171ad7aaf 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs
@@ -8,6 +8,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@@ -32,7 +33,7 @@ namespace FancyZonesEditor
InitializeComponent();
Loaded += GridEditor_Loaded;
Unloaded += GridEditor_Unloaded;
- ((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
+ ((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
}
@@ -69,7 +70,8 @@ namespace FancyZonesEditor
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
- Size actualSize = new Size(ActualWidth, ActualHeight);
+ 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)
@@ -248,7 +250,7 @@ namespace FancyZonesEditor
}
_dragHandles.UpdateAfterVerticalSplit(foundCol);
- _data.SplitColumn(foundCol, spliteeIndex, newChildIndex, space, offset, ActualWidth);
+ _data.SplitColumn(foundCol, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Width);
_dragHandles.AddDragHandle(Orientation.Vertical, foundRow, foundCol, model);
}
else
@@ -298,11 +300,12 @@ namespace FancyZonesEditor
}
_dragHandles.UpdateAfterHorizontalSplit(foundRow);
- _data.SplitRow(foundRow, spliteeIndex, newChildIndex, space, offset, ActualHeight);
+ _data.SplitRow(foundRow, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Height);
_dragHandles.AddDragHandle(Orientation.Horizontal, foundRow, foundCol, model);
}
- Size actualSize = new Size(ActualWidth, ActualHeight);
+ var workArea = App.Overlay.WorkArea;
+ Size actualSize = new Size(workArea.Width, workArea.Height);
ArrangeGridRects(actualSize);
}
@@ -354,7 +357,8 @@ namespace FancyZonesEditor
private void OnGridDimensionsChanged()
{
- Size actualSize = new Size(ActualWidth, ActualHeight);
+ Rect workingArea = App.Overlay.WorkArea;
+ Size actualSize = new Size(workingArea.Width, workingArea.Height);
if (actualSize.Width > 0)
{
ArrangeGridRects(actualSize);
@@ -363,6 +367,10 @@ namespace FancyZonesEditor
private void ArrangeGridRects(Size arrangeSize)
{
+ var workArea = App.Overlay.WorkArea;
+ Preview.Width = workArea.Width;
+ Preview.Height = workArea.Height;
+
GridLayoutModel model = Model;
if (model == null)
{
@@ -375,7 +383,7 @@ namespace FancyZonesEditor
return;
}
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
@@ -403,7 +411,7 @@ namespace FancyZonesEditor
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
{
double spacing = 0;
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (settings.ShowSpacing)
{
spacing = settings.Spacing;
@@ -422,7 +430,8 @@ namespace FancyZonesEditor
}
}
- Size actualSize = new Size(ActualWidth, ActualHeight);
+ Rect workingArea = App.Overlay.WorkArea;
+ Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
AdornerLayer.UpdateLayout();
}
@@ -433,7 +442,8 @@ namespace FancyZonesEditor
int index = _data.SwappedIndexAfterResize(resizer);
if (index != -1)
{
- Size actualSize = new Size(ActualWidth, ActualHeight);
+ Rect workingArea = App.Overlay.WorkArea;
+ Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
}
}
@@ -478,7 +488,6 @@ namespace FancyZonesEditor
if (_startDragPos.X != -1)
{
Point dragPos = e.GetPosition(Preview);
-
_startRow = -1;
_endRow = -1;
_startCol = -1;
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml
index 4699192a39..5a35cf349b 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml
@@ -13,7 +13,7 @@
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
- WindowStartupLocation="CenterScreen"
+ WindowStartupLocation="CenterOwner"
Closed="OnClosed">
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml.cs
index fe66febc90..417bbc61cc 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditorWindow.xaml.cs
@@ -5,6 +5,7 @@
using System.Windows;
using System.Windows.Input;
using FancyZonesEditor.Models;
+using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@@ -19,13 +20,13 @@ namespace FancyZonesEditor
KeyUp += GridEditorWindow_KeyUp;
- _stashedModel = (GridLayoutModel)(EditorOverlay.Current.DataContext as GridLayoutModel).Clone();
+ _stashedModel = (GridLayoutModel)(App.Overlay.CurrentDataContext as GridLayoutModel).Clone();
}
protected new void OnCancel(object sender, RoutedEventArgs e)
{
base.OnCancel(sender, e);
- GridLayoutModel model = EditorOverlay.Current.DataContext as GridLayoutModel;
+ GridLayoutModel model = App.Overlay.CurrentDataContext as GridLayoutModel;
_stashedModel.RestoreTo(model);
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridZone.xaml.cs
index a87768cbd2..837a6f2ab0 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/GridZone.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridZone.xaml.cs
@@ -69,14 +69,14 @@ namespace FancyZonesEditor
};
Body.Children.Add(_splitter);
- ((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
+ ((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == PropertyIsShiftKeyPressedID)
{
- _switchOrientation = ((App)Application.Current).ZoneSettings.IsShiftKeyPressed;
+ _switchOrientation = ((App)Application.Current).MainWindowSettings.IsShiftKeyPressed;
if (_lastPos.X != -1)
{
UpdateSplitter();
@@ -108,7 +108,7 @@ namespace FancyZonesEditor
{
get
{
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (!settings.ShowSpacing)
{
return 1;
@@ -283,7 +283,7 @@ namespace FancyZonesEditor
private void DoSplit(Orientation orientation, double offset)
{
int spacing = 0;
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (settings.ShowSpacing)
{
spacing = settings.Spacing;
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml
similarity index 73%
rename from src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml
rename to src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml
index 19c79f6d8c..2ccd224aa2 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorOverlay.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml
@@ -1,14 +1,13 @@
-
+ Background="Transparent"/>
\ No newline at end of file
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml.cs
new file mode 100644
index 0000000000..0f5e8e762b
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutOverlayWindow.xaml.cs
@@ -0,0 +1,21 @@
+// 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.Windows;
+using System.Windows.Media;
+
+namespace FancyZonesEditor
+{
+ ///
+ /// Interaction logic for LayoutOverlayWindow.xaml
+ ///
+ public partial class LayoutOverlayWindow : Window
+ {
+ public LayoutOverlayWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs
index 6bd4037b0d..0ff7585fc7 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// 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.
@@ -29,11 +29,22 @@ namespace FancyZonesEditor
private LayoutModel _model;
private List _zones = new List();
+ public bool IsActualSize
+ {
+ get { return (bool)GetValue(IsActualSizeProperty); }
+ set { SetValue(IsActualSizeProperty, value); }
+ }
+
public LayoutPreview()
{
InitializeComponent();
DataContextChanged += LayoutPreview_DataContextChanged;
- ((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
+ ((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
+ }
+
+ public void UpdatePreview()
+ {
+ RenderPreview();
}
private void LayoutPreview_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
@@ -42,12 +53,6 @@ namespace FancyZonesEditor
RenderPreview();
}
- public bool IsActualSize
- {
- get { return (bool)GetValue(IsActualSizeProperty); }
- set { SetValue(IsActualSizeProperty, value); }
- }
-
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == PropertyZoneCountID)
@@ -109,27 +114,25 @@ namespace FancyZonesEditor
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
select new RowColInfo(percent)).ToArray();
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
- int width = (int)Settings.WorkArea.Width;
- int height = (int)Settings.WorkArea.Height;
-
- double totalWidth = width - (spacing * (cols + 1));
- double totalHeight = height - (spacing * (rows + 1));
+ 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, totalHeight);
+ 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, totalWidth);
+ double cellWidth = colInfo[col].Recalculate(left, width);
left += cellWidth + spacing;
}
@@ -140,8 +143,8 @@ namespace FancyZonesEditor
Body.Children.Add(viewbox);
Canvas frame = new Canvas
{
- Width = width,
- Height = height,
+ Width = workArea.Width,
+ Height = workArea.Height,
};
viewbox.Child = frame;
@@ -183,6 +186,14 @@ namespace FancyZonesEditor
}
}
}
+
+ if (App.DebugMode)
+ {
+ TextBlock text = new TextBlock();
+ text.Text = "(" + workArea.X + "," + workArea.Y + ")";
+ text.FontSize = 42;
+ frame.Children.Add(text);
+ }
}
private void RenderSmallScalePreview(GridLayoutModel grid)
@@ -205,7 +216,7 @@ namespace FancyZonesEditor
Body.ColumnDefinitions.Add(def);
}
- Settings settings = ((App)Application.Current).ZoneSettings;
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
Thickness margin = new Thickness(settings.ShowSpacing ? settings.Spacing / 20 : 0);
List visited = new List();
@@ -265,6 +276,12 @@ namespace FancyZonesEditor
private void RenderCanvasPreview(CanvasLayoutModel canvas)
{
+ var workArea = canvas.CanvasRect;
+ if (workArea.Width == 0 || workArea.Height == 0 || App.Overlay.SpanZonesAcrossMonitors)
+ {
+ workArea = App.Overlay.WorkArea;
+ }
+
Viewbox viewbox = new Viewbox
{
Stretch = Stretch.Uniform,
@@ -272,10 +289,11 @@ namespace FancyZonesEditor
Body.Children.Add(viewbox);
Canvas frame = new Canvas
{
- Width = Settings.WorkArea.Width,
- Height = Settings.WorkArea.Height,
+ Width = workArea.Width,
+ Height = workArea.Height,
};
viewbox.Child = frame;
+
foreach (Int32Rect zone in canvas.Zones)
{
Rectangle rect = new Rectangle();
@@ -288,6 +306,14 @@ namespace FancyZonesEditor
rect.Fill = Brushes.LightGray;
frame.Children.Add(rect);
}
+
+ if (App.DebugMode)
+ {
+ TextBlock text = new TextBlock();
+ text.Text = "(" + App.Overlay.WorkArea.X + "," + App.Overlay.WorkArea.Y + ")";
+ text.FontSize = 42;
+ frame.Children.Add(text);
+ }
}
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml
index c20e72d03a..ac48c2b37e 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml
@@ -1,324 +1,464 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
index 4a94e08d7a..e036dc8bd8 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -19,12 +18,16 @@ namespace FancyZonesEditor
{
// TODO: share the constants b/w C# Editor and FancyZoneLib
public const int MaxZones = 40;
- private readonly Settings _settings = ((App)Application.Current).ZoneSettings;
+ private const int DefaultWrapPanelItemSize = 262;
+ private const int SmallWrapPanelItemSize = 180;
+ private const int MinimalForDefaultWrapPanelsHeight = 900;
+
+ private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings;
// Localizable string
private static readonly string _defaultNamePrefix = "Custom Layout ";
- public int WrapPanelItemSize { get; set; } = 262;
+ public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize;
public double SettingsTextMaxWidth
{
@@ -34,20 +37,31 @@ namespace FancyZonesEditor
}
}
- public MainWindow()
+ public MainWindow(bool spanZonesAcrossMonitors, Rect workArea)
{
InitializeComponent();
DataContext = _settings;
KeyUp += MainWindow_KeyUp;
- if (Settings.WorkArea.Height < 900)
+ if (spanZonesAcrossMonitors)
+ {
+ WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ }
+
+ if (workArea.Height < MinimalForDefaultWrapPanelsHeight || App.Overlay.MultiMonitorMode)
{
SizeToContent = SizeToContent.WidthAndHeight;
- WrapPanelItemSize = 180;
+ WrapPanelItemSize = SmallWrapPanelItemSize;
}
}
+ public void Update()
+ {
+ DataContext = _settings;
+ SetSelectedItem();
+ }
+
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
@@ -101,19 +115,19 @@ namespace FancyZonesEditor
private void Select(LayoutModel newSelection)
{
- if (EditorOverlay.Current.DataContext is LayoutModel currentSelection)
+ if (App.Overlay.CurrentDataContext is LayoutModel currentSelection)
{
currentSelection.IsSelected = false;
}
newSelection.IsSelected = true;
- EditorOverlay.Current.DataContext = newSelection;
+ App.Overlay.CurrentDataContext = newSelection;
}
private void EditLayout_Click(object sender, RoutedEventArgs e)
{
- EditorOverlay mainEditor = EditorOverlay.Current;
- if (!(mainEditor.DataContext is LayoutModel model))
+ var mainEditor = App.Overlay;
+ if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
return;
}
@@ -121,19 +135,19 @@ namespace FancyZonesEditor
model.IsSelected = false;
Hide();
- bool isPredefinedLayout = Settings.IsPredefinedLayout(model);
+ bool isPredefinedLayout = MainWindowSettingsModel.IsPredefinedLayout(model);
- if (!Settings.CustomModels.Contains(model) || isPredefinedLayout)
+ if (!MainWindowSettingsModel.CustomModels.Contains(model) || isPredefinedLayout)
{
if (isPredefinedLayout)
{
// make a copy
model = model.Clone();
- mainEditor.DataContext = model;
+ mainEditor.CurrentDataContext = model;
}
int maxCustomIndex = 0;
- foreach (LayoutModel customModel in Settings.CustomModels)
+ foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
{
string name = customModel.Name;
if (name.StartsWith(_defaultNamePrefix))
@@ -151,32 +165,7 @@ namespace FancyZonesEditor
model.Name = _defaultNamePrefix + (++maxCustomIndex);
}
- mainEditor.Edit();
-
- EditorWindow window;
- bool isGrid = false;
- if (model is GridLayoutModel)
- {
- window = new GridEditorWindow();
- isGrid = true;
- }
- else
- {
- window = new CanvasEditorWindow();
- }
-
- window.Owner = EditorOverlay.Current;
-
- window.DataContext = model;
- window.Show();
-
- if (isGrid)
- {
- (window as GridEditorWindow).NameTextBox().Focus();
- }
-
- window.LeftWindowCommands = null;
- window.RightWindowCommands = null;
+ mainEditor.OpenEditor(model);
}
private void Apply_Click(object sender, RoutedEventArgs e)
@@ -186,20 +175,16 @@ namespace FancyZonesEditor
private void Apply()
{
- EditorOverlay mainEditor = EditorOverlay.Current;
+ ((App)Application.Current).MainWindowSettings.ResetAppliedModel();
- if (mainEditor.DataContext is LayoutModel model)
+ var mainEditor = App.Overlay;
+ if (mainEditor.CurrentDataContext is LayoutModel model)
{
- // If custom canvas layout has been scaled, persisting is needed
- if (model is CanvasLayoutModel && (model as CanvasLayoutModel).IsScaled)
- {
- model.Persist();
- }
- else
- {
- model.Apply();
- }
+ model.Apply();
+ }
+ if (!mainEditor.MultiMonitorMode)
+ {
Close();
}
}
@@ -207,7 +192,7 @@ namespace FancyZonesEditor
private void OnClosing(object sender, EventArgs e)
{
LayoutModel.SerializeDeletedCustomZoneSets();
- EditorOverlay.Current.Close();
+ App.Overlay.CloseLayoutWindow();
}
private void OnInitialized(object sender, EventArgs e)
@@ -217,7 +202,7 @@ namespace FancyZonesEditor
private void SetSelectedItem()
{
- foreach (LayoutModel model in Settings.CustomModels)
+ foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
{
if (model.IsSelected)
{
@@ -237,5 +222,25 @@ namespace FancyZonesEditor
model.Delete();
}
+
+ private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ ScrollViewer scrollviewer = sender as ScrollViewer;
+ if (e.Delta > 0)
+ {
+ scrollviewer.LineLeft();
+ }
+ else
+ {
+ scrollviewer.LineRight();
+ }
+
+ e.Handled = true;
+ }
+
+ private void CloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ this.Close();
+ }
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs
index fecda82b32..3af612ddfb 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs
@@ -1,10 +1,9 @@
-// Copyright (c) Microsoft Corporation
+// 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.IO.Abstractions;
using System.Text.Json;
using System.Windows;
@@ -14,33 +13,21 @@ namespace FancyZonesEditor.Models
// Free form Layout Model, which specifies independent zone rects
public class CanvasLayoutModel : LayoutModel
{
- // Localizable strings
- private const string ErrorPersistingCanvasLayout = "Error persisting canvas layout";
-
// Non-localizable strings
private const string ModelTypeID = "canvas";
- public CanvasLayoutModel(string uuid, string name, LayoutType type, IList zones, int workAreaWidth, int workAreaHeight)
+ public Rect CanvasRect { get; private set; }
+
+ public CanvasLayoutModel(string uuid, string name, LayoutType type, IList zones, int width, int height)
: base(uuid, name, type)
{
- lastWorkAreaWidth = workAreaWidth;
- lastWorkAreaHeight = workAreaHeight;
- IsScaled = false;
-
- if (ShouldScaleLayout())
- {
- ScaleLayout(zones);
- }
- else
- {
- Zones = zones;
- }
+ Zones = zones;
+ CanvasRect = new Rect(new Size(width, height));
}
public CanvasLayoutModel(string name, LayoutType type)
: base(name, type)
{
- IsScaled = false;
}
public CanvasLayoutModel(string name)
@@ -51,12 +38,6 @@ namespace FancyZonesEditor.Models
// Zones - the list of all zones in this layout, described as independent rectangles
public IList Zones { get; private set; } = new List();
- private int lastWorkAreaWidth = (int)Settings.WorkArea.Width;
-
- private int lastWorkAreaHeight = (int)Settings.WorkArea.Height;
-
- public bool IsScaled { get; private set; }
-
// RemoveZoneAt
// Removes the specified index from the Zones list, and fires a property changed notification for the Zones property
public void RemoveZoneAt(int index)
@@ -102,34 +83,6 @@ namespace FancyZonesEditor.Models
}
}
- private bool ShouldScaleLayout()
- {
- // Scale if:
- // - at least one dimension changed
- // - orientation remained the same
- return (lastWorkAreaHeight != Settings.WorkArea.Height || lastWorkAreaWidth != Settings.WorkArea.Width) &&
- ((lastWorkAreaHeight > lastWorkAreaWidth && Settings.WorkArea.Height > Settings.WorkArea.Width) ||
- (lastWorkAreaWidth > lastWorkAreaHeight && Settings.WorkArea.Width > Settings.WorkArea.Height));
- }
-
- private void ScaleLayout(IList zones)
- {
- foreach (Int32Rect zone in zones)
- {
- double widthFactor = (double)Settings.WorkArea.Width / lastWorkAreaWidth;
- double heightFactor = (double)Settings.WorkArea.Height / lastWorkAreaHeight;
- int scaledX = (int)(zone.X * widthFactor);
- int scaledY = (int)(zone.Y * heightFactor);
- int scaledWidth = (int)(zone.Width * widthFactor);
- int scaledHeight = (int)(zone.Height * heightFactor);
- Zones.Add(new Int32Rect(scaledX, scaledY, scaledWidth, scaledHeight));
- }
-
- lastWorkAreaHeight = (int)Settings.WorkArea.Height;
- lastWorkAreaWidth = (int)Settings.WorkArea.Width;
- IsScaled = true;
- }
-
private struct Zone
{
public int X { get; set; }
@@ -165,13 +118,15 @@ namespace FancyZonesEditor.Models
// Implements the LayoutModel.PersistData abstract method
protected override void PersistData()
{
+ AddCustomLayout(this);
+
CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo
{
- RefWidth = lastWorkAreaWidth,
- RefHeight = lastWorkAreaHeight,
-
+ RefWidth = (int)CanvasRect.Width,
+ RefHeight = (int)CanvasRect.Height,
Zones = new Zone[Zones.Count],
};
+
for (int i = 0; i < Zones.Count; ++i)
{
Zone zone = new Zone
@@ -187,26 +142,19 @@ namespace FancyZonesEditor.Models
CanvasLayoutJson jsonObj = new CanvasLayoutJson
{
- Uuid = "{" + Guid.ToString().ToUpper() + "}",
+ Uuid = Uuid,
Name = Name,
Type = ModelTypeID,
Info = layoutInfo,
};
-
JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
};
- try
- {
- string jsonString = JsonSerializer.Serialize(jsonObj, options);
- FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
- }
- catch (Exception ex)
- {
- ShowExceptionMessageBox(ErrorPersistingCanvasLayout, ex);
- }
+ string jsonString = JsonSerializer.Serialize(jsonObj, options);
+ AddCustomLayoutJson(JsonSerializer.Deserialize(jsonString));
+ SerializeCreatedCustomZonesets();
}
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Device.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Device.cs
new file mode 100644
index 0000000000..00031f170f
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Device.cs
@@ -0,0 +1,81 @@
+// 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.Text;
+using System.Windows;
+
+namespace FancyZonesEditor.Utils
+{
+ public class Device
+ {
+ public string Id { get; set; }
+
+ public Rect UnscaledBounds { get; private set; }
+
+ public Rect ScaledBounds { get; private set; }
+
+ public Rect WorkAreaRect { get; private set; }
+
+ public int Dpi { get; set; }
+
+ public bool Primary { get; private set; }
+
+ public Device(string id, int dpi, Rect bounds, Rect workArea, bool primary)
+ {
+ Id = id;
+ Dpi = dpi;
+ WorkAreaRect = workArea;
+ UnscaledBounds = bounds;
+ ScaledBounds = bounds;
+ Primary = primary;
+ }
+
+ public Device(Rect bounds, Rect workArea, bool primary)
+ {
+ WorkAreaRect = workArea;
+ UnscaledBounds = bounds;
+ ScaledBounds = bounds;
+ Primary = primary;
+ }
+
+ public void Scale(double scaleFactor)
+ {
+ WorkAreaRect = new Rect(Math.Round(WorkAreaRect.X * scaleFactor), Math.Round(WorkAreaRect.Y * scaleFactor), Math.Round(WorkAreaRect.Width * scaleFactor), Math.Round(WorkAreaRect.Height * scaleFactor));
+ ScaledBounds = new Rect(Math.Round(ScaledBounds.X * scaleFactor), Math.Round(ScaledBounds.Y * scaleFactor), Math.Round(ScaledBounds.Width * scaleFactor), Math.Round(ScaledBounds.Height * scaleFactor));
+ }
+
+ public double ScaleCoordinate(double coordinate)
+ {
+ float dpi = Dpi != 0 ? Dpi : 96f;
+ double scaleFactor = 96f / dpi;
+ return Math.Round(coordinate * scaleFactor);
+ }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.Append("ID: ");
+ sb.AppendLine(Id);
+ sb.Append("DPI: ");
+ sb.AppendLine(Dpi.ToString());
+ sb.Append("Is primary: ");
+ sb.AppendLine(Primary.ToString());
+
+ string workArea = string.Format("({0}, {1}, {2}, {3})", WorkAreaRect.X, WorkAreaRect.Y, WorkAreaRect.Width, WorkAreaRect.Height);
+ string bounds = string.Format("({0}, {1}, {2}, {3})", UnscaledBounds.X, UnscaledBounds.Y, UnscaledBounds.Width, UnscaledBounds.Height);
+ string scaledBounds = string.Format("({0}, {1}, {2}, {3})", ScaledBounds.X, ScaledBounds.Y, ScaledBounds.Width, ScaledBounds.Height);
+
+ sb.Append("Work area: ");
+ sb.AppendLine(workArea);
+ sb.Append("Unscaled bounds: ");
+ sb.AppendLine(bounds);
+ sb.Append("Scaled bounds: ");
+ sb.AppendLine(scaledBounds);
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs
index abcd3b2de9..1dbc391e13 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs
@@ -13,9 +13,6 @@ namespace FancyZonesEditor.Models
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
public class GridLayoutModel : LayoutModel
{
- // Localizable strings
- private const string ErrorPersistingGridLayout = "Error persisting grid layout";
-
// Non-localizable strings
private const string ModelTypeID = "grid";
@@ -204,6 +201,8 @@ namespace FancyZonesEditor.Models
// Implements the LayoutModel.PersistData abstract method
protected override void PersistData()
{
+ AddCustomLayout(this);
+
GridLayoutInfo layoutInfo = new GridLayoutInfo
{
Rows = Rows,
@@ -212,6 +211,7 @@ namespace FancyZonesEditor.Models
ColumnsPercentage = ColumnPercents,
CellChildMap = new int[Rows][],
};
+
for (int row = 0; row < Rows; row++)
{
layoutInfo.CellChildMap[row] = new int[Columns];
@@ -223,7 +223,7 @@ namespace FancyZonesEditor.Models
GridLayoutJson jsonObj = new GridLayoutJson
{
- Uuid = "{" + Guid.ToString().ToUpper() + "}",
+ Uuid = Uuid,
Name = Name,
Type = ModelTypeID,
Info = layoutInfo,
@@ -233,15 +233,9 @@ namespace FancyZonesEditor.Models
PropertyNamingPolicy = new DashCaseNamingPolicy(),
};
- try
- {
- string jsonString = JsonSerializer.Serialize(jsonObj, options);
- FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
- }
- catch (Exception ex)
- {
- ShowExceptionMessageBox(ErrorPersistingGridLayout, ex);
- }
+ string jsonString = JsonSerializer.Serialize(jsonObj, options);
+ AddCustomLayoutJson(JsonSerializer.Deserialize(jsonString));
+ SerializeCreatedCustomZonesets();
}
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs
index 1a286505ea..f921afb5d2 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutModel.cs
@@ -6,77 +6,15 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
-using System.IO;
-using System.IO.Abstractions;
-using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json;
-using System.Windows;
namespace FancyZonesEditor.Models
{
- public enum LayoutType
- {
- Blank = -1,
- Focus,
- Columns,
- Rows,
- Grid,
- PriorityGrid,
- Custom,
- }
-
// Base LayoutModel
// Manages common properties and base persistence
public abstract class LayoutModel : INotifyPropertyChanged
{
- protected static readonly IFileSystem FileSystem = new FileSystem();
-
- // Localizable strings
- private const string ErrorMessageBoxTitle = "FancyZones Editor Exception Handler";
- private const string ErrorMessageBoxMessage = "Please report the bug to ";
- private const string ErrorLayoutMalformedData = "Layout '{0}' has malformed data";
- private const string ErrorSerializingDeletedLayouts = "Error serializing deleted layouts";
- private const string ErrorLoadingCustomLayouts = "Error loading custom layouts";
- private const string ErrorApplyingLayout = "Error applying layout";
-
- // Non-localizable strings
- private const string NameStr = "name";
- private const string CustomZoneSetsJsonTag = "custom-zone-sets";
- private const string TypeJsonTag = "type";
- private const string UuidJsonTag = "uuid";
- private const string InfoJsonTag = "info";
- private const string GridJsonTag = "grid";
- private const string RowsJsonTag = "rows";
- private const string ColumnsJsonTag = "columns";
- private const string RowsPercentageJsonTag = "rows-percentage";
- private const string ColumnsPercentageJsonTag = "columns-percentage";
- private const string CellChildMapJsonTag = "cell-child-map";
- private const string ZonesJsonTag = "zones";
- private const string CanvasJsonTag = "canvas";
- private const string RefWidthJsonTag = "ref-width";
- private const string RefHeightJsonTag = "ref-height";
- private const string XJsonTag = "X";
- private const string YJsonTag = "Y";
- private const string WidthJsonTag = "width";
- private const string HeightJsonTag = "height";
- private const string FocusJsonTag = "focus";
- private const string PriorityGridJsonTag = "priority-grid";
- private const string CustomJsonTag = "custom";
-
- private const string PowerToysIssuesURL = "https://aka.ms/powerToysReportBug";
-
- public static void ShowExceptionMessageBox(string message, Exception exception = null)
- {
- string fullMessage = ErrorMessageBoxMessage + PowerToysIssuesURL + " \n" + message;
- if (exception != null)
- {
- fullMessage += ": " + exception.Message;
- }
-
- MessageBox.Show(fullMessage, ErrorMessageBoxTitle);
- }
-
protected LayoutModel()
{
_guid = Guid.NewGuid();
@@ -136,6 +74,14 @@ namespace FancyZonesEditor.Models
private Guid _guid;
+ public string Uuid
+ {
+ get
+ {
+ return "{" + Guid.ToString().ToUpper() + "}";
+ }
+ }
+
// 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
@@ -157,6 +103,25 @@ namespace FancyZonesEditor.Models
private bool _isSelected;
+ public bool IsApplied
+ {
+ get
+ {
+ return _isApplied;
+ }
+
+ set
+ {
+ if (_isApplied != value)
+ {
+ _isApplied = value;
+ FirePropertyChanged();
+ }
+ }
+ }
+
+ private bool _isApplied;
+
// implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
@@ -177,186 +142,52 @@ namespace FancyZonesEditor.Models
}
}
- private struct DeletedCustomZoneSetsWrapper
+ // Adds new custom Layout
+ public void AddCustomLayout(LayoutModel model)
{
- public List DeletedCustomZoneSets { get; set; }
+ bool updated = false;
+ for (int i = 0; i < _customModels.Count && !updated; i++)
+ {
+ if (_customModels[i].Uuid == model.Uuid)
+ {
+ _customModels[i] = model;
+ updated = true;
+ }
+ }
+
+ if (!updated)
+ {
+ _customModels.Add(model);
+ }
+ }
+
+ // Add custom layouts json data that would be serialized to a temp file
+ public void AddCustomLayoutJson(JsonElement json)
+ {
+ _createdCustomLayouts.Add(json);
}
public static void SerializeDeletedCustomZoneSets()
{
- DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper
- {
- DeletedCustomZoneSets = _deletedCustomModels,
- };
+ App.FancyZonesEditorIO.SerializeDeletedCustomZoneSets(_deletedCustomModels);
+ }
- JsonSerializerOptions options = new JsonSerializerOptions
- {
- PropertyNamingPolicy = new DashCaseNamingPolicy(),
- };
-
- try
- {
- string jsonString = JsonSerializer.Serialize(deletedLayouts, options);
- FileSystem.File.WriteAllText(Settings.DeletedCustomZoneSetsTmpFile, jsonString);
- }
- catch (Exception ex)
- {
- ShowExceptionMessageBox(ErrorSerializingDeletedLayouts, ex);
- }
+ public static void SerializeCreatedCustomZonesets()
+ {
+ App.FancyZonesEditorIO.SerializeCreatedCustomZonesets(_createdCustomLayouts);
}
// Loads all the custom Layouts from tmp file passed by FancyZonesLib
public static ObservableCollection LoadCustomModels()
{
_customModels = new ObservableCollection();
-
- try
- {
- Stream inputStream = FileSystem.File.Open(Settings.FancyZonesSettingsFile, FileMode.Open);
- JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default);
- JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty(CustomZoneSetsJsonTag).EnumerateArray();
-
- while (customZoneSetsEnumerator.MoveNext())
- {
- var current = customZoneSetsEnumerator.Current;
- string name = current.GetProperty(NameStr).GetString();
- string type = current.GetProperty(TypeJsonTag).GetString();
- string uuid = current.GetProperty(UuidJsonTag).GetString();
- var info = current.GetProperty(InfoJsonTag);
-
- if (type.Equals(GridJsonTag))
- {
- bool error = false;
-
- int rows = info.GetProperty(RowsJsonTag).GetInt32();
- int columns = info.GetProperty(ColumnsJsonTag).GetInt32();
-
- List rowsPercentage = new List(rows);
- JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty(RowsPercentageJsonTag).EnumerateArray();
-
- List columnsPercentage = new List(columns);
- JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty(ColumnsPercentageJsonTag).EnumerateArray();
-
- if (rows <= 0 || columns <= 0 || rowsPercentageEnumerator.Count() != rows || columnsPercentageEnumerator.Count() != columns)
- {
- error = true;
- }
-
- while (!error && rowsPercentageEnumerator.MoveNext())
- {
- int percentage = rowsPercentageEnumerator.Current.GetInt32();
- if (percentage <= 0)
- {
- error = true;
- break;
- }
-
- rowsPercentage.Add(percentage);
- }
-
- while (!error && columnsPercentageEnumerator.MoveNext())
- {
- int percentage = columnsPercentageEnumerator.Current.GetInt32();
- if (percentage <= 0)
- {
- error = true;
- break;
- }
-
- columnsPercentage.Add(percentage);
- }
-
- int i = 0;
- JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty(CellChildMapJsonTag).EnumerateArray();
- int[,] cellChildMap = new int[rows, columns];
-
- if (cellChildMapRows.Count() != rows)
- {
- error = true;
- }
-
- while (!error && cellChildMapRows.MoveNext())
- {
- int j = 0;
- JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray();
- if (cellChildMapRowElems.Count() != columns)
- {
- error = true;
- break;
- }
-
- while (cellChildMapRowElems.MoveNext())
- {
- cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32();
- }
-
- i++;
- }
-
- if (error)
- {
- ShowExceptionMessageBox(string.Format(ErrorLayoutMalformedData, name));
- _deletedCustomModels.Add(Guid.Parse(uuid).ToString().ToUpper());
- continue;
- }
-
- _customModels.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap));
- }
- else if (type.Equals(CanvasJsonTag))
- {
- int lastWorkAreaWidth = info.GetProperty(RefWidthJsonTag).GetInt32();
- int lastWorkAreaHeight = info.GetProperty(RefHeightJsonTag).GetInt32();
-
- JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty(ZonesJsonTag).EnumerateArray();
- IList zones = new List();
-
- bool error = false;
-
- if (lastWorkAreaWidth <= 0 || lastWorkAreaHeight <= 0)
- {
- error = true;
- }
-
- while (!error && zonesEnumerator.MoveNext())
- {
- int x = zonesEnumerator.Current.GetProperty(XJsonTag).GetInt32();
- int y = zonesEnumerator.Current.GetProperty(YJsonTag).GetInt32();
- int width = zonesEnumerator.Current.GetProperty(WidthJsonTag).GetInt32();
- int height = zonesEnumerator.Current.GetProperty(HeightJsonTag).GetInt32();
-
- if (width <= 0 || height <= 0)
- {
- error = true;
- break;
- }
-
- zones.Add(new Int32Rect(x, y, width, height));
- }
-
- if (error)
- {
- ShowExceptionMessageBox(string.Format(ErrorLayoutMalformedData, name));
- _deletedCustomModels.Add(Guid.Parse(uuid).ToString().ToUpper());
- continue;
- }
-
- _customModels.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, zones, lastWorkAreaWidth, lastWorkAreaHeight));
- }
- }
-
- inputStream.Close();
- }
- catch (Exception ex)
- {
- ShowExceptionMessageBox(ErrorLoadingCustomLayouts, ex);
- return new ObservableCollection();
- }
-
+ App.FancyZonesEditorIO.ParseLayouts(ref _customModels, ref _deletedCustomModels);
return _customModels;
}
private static ObservableCollection _customModels = null;
private static List _deletedCustomModels = new List();
+ private static List _createdCustomLayouts = new List();
// Callbacks that the base LayoutModel makes to derived types
protected abstract void PersistData();
@@ -369,83 +200,18 @@ namespace FancyZonesEditor.Models
Apply();
}
- private struct ActiveZoneSetWrapper
- {
- public string Uuid { get; set; }
-
- public string Type { get; set; }
- }
-
- private struct AppliedZoneSet
- {
- public string DeviceId { get; set; }
-
- public ActiveZoneSetWrapper ActiveZoneset { get; set; }
-
- public bool EditorShowSpacing { get; set; }
-
- public int EditorSpacing { get; set; }
-
- public int EditorZoneCount { get; set; }
-
- public int EditorSensitivityRadius { get; set; }
- }
-
public void Apply()
{
- ActiveZoneSetWrapper activeZoneSet = new ActiveZoneSetWrapper
- {
- Uuid = "{" + Guid.ToString().ToUpper() + "}",
- };
+ MainWindowSettingsModel settings = ((App)App.Current).MainWindowSettings;
+ settings.ResetAppliedModel();
+ IsApplied = true;
- switch (Type)
- {
- case LayoutType.Focus:
- activeZoneSet.Type = FocusJsonTag;
- break;
- case LayoutType.Rows:
- activeZoneSet.Type = RowsJsonTag;
- break;
- case LayoutType.Columns:
- activeZoneSet.Type = ColumnsJsonTag;
- break;
- case LayoutType.Grid:
- activeZoneSet.Type = GridJsonTag;
- break;
- case LayoutType.PriorityGrid:
- activeZoneSet.Type = PriorityGridJsonTag;
- break;
- case LayoutType.Custom:
- activeZoneSet.Type = CustomJsonTag;
- break;
- }
+ // update settings
+ App.Overlay.CurrentLayoutSettings.ZonesetUuid = Uuid;
+ App.Overlay.CurrentLayoutSettings.Type = Type;
- Settings settings = ((App)Application.Current).ZoneSettings;
-
- AppliedZoneSet zoneSet = new AppliedZoneSet
- {
- DeviceId = Settings.UniqueKey,
- ActiveZoneset = activeZoneSet,
- EditorShowSpacing = settings.ShowSpacing,
- EditorSpacing = settings.Spacing,
- EditorZoneCount = settings.ZoneCount,
- EditorSensitivityRadius = settings.SensitivityRadius,
- };
-
- JsonSerializerOptions options = new JsonSerializerOptions
- {
- PropertyNamingPolicy = new DashCaseNamingPolicy(),
- };
-
- try
- {
- string jsonString = JsonSerializer.Serialize(zoneSet, options);
- FileSystem.File.WriteAllText(Settings.ActiveZoneSetTmpFile, jsonString);
- }
- catch (Exception ex)
- {
- ShowExceptionMessageBox(ErrorApplyingLayout, ex);
- }
+ // update temp file
+ App.FancyZonesEditorIO.SerializeAppliedLayouts();
}
}
}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs
new file mode 100644
index 0000000000..eea1dc3977
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutSettings.cs
@@ -0,0 +1,33 @@
+// 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 FancyZonesEditor.Models;
+
+namespace FancyZonesEditor
+{
+ public class LayoutSettings
+ {
+ public static bool DefaultShowSpacing => true;
+
+ public static int DefaultSpacing => 16;
+
+ public static int DefaultZoneCount => 3;
+
+ public static int DefaultSensitivityRadius => 20;
+
+ public string DeviceId { get; set; } = string.Empty;
+
+ public string ZonesetUuid { get; set; } = string.Empty;
+
+ public LayoutType Type { get; set; } = LayoutType.PriorityGrid;
+
+ public bool ShowSpacing { get; set; } = DefaultShowSpacing;
+
+ public int Spacing { get; set; } = DefaultSpacing;
+
+ public int ZoneCount { get; set; } = DefaultZoneCount;
+
+ public int SensitivityRadius { get; set; } = DefaultSensitivityRadius;
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutType.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutType.cs
new file mode 100644
index 0000000000..d9a51bc0f6
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/LayoutType.cs
@@ -0,0 +1,17 @@
+// 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.Models
+{
+ public enum LayoutType
+ {
+ Blank = -1,
+ Focus,
+ Columns,
+ Rows,
+ Grid,
+ PriorityGrid,
+ Custom,
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
similarity index 56%
rename from src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs
rename to src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
index c5229ee555..85334d286a 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Settings.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// 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.
@@ -6,10 +6,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
-using System.IO;
-using System.IO.Abstractions;
using System.Runtime.CompilerServices;
-using System.Text.Json;
using System.Windows;
using FancyZonesEditor.Models;
@@ -18,30 +15,16 @@ 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 class MainWindowSettingsModel : INotifyPropertyChanged
{
- private enum CmdArgs
+ private enum DeviceIdParts
{
- WorkAreaSize = 1,
- PowerToysPID,
- }
-
- private enum WorkAreaCmdArgElements
- {
- X = 0,
- Y,
+ Name = 0,
Width,
Height,
+ VirtualDesktopId,
}
- private enum ParseDeviceMode
- {
- Prod,
- Debug,
- }
-
- private static readonly IFileSystem _fileSystem = new FileSystem();
-
private static CanvasLayoutModel _blankCustomModel;
private readonly CanvasLayoutModel _focusModel;
private readonly GridLayoutModel _rowsModel;
@@ -63,33 +46,9 @@ namespace FancyZonesEditor
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
- private const string ZonesSettingsFile = "\\Microsoft\\PowerToys\\FancyZones\\zones-settings.json";
- private const string ActiveZoneSetsTmpFileName = "FancyZonesActiveZoneSets.json";
- private const string AppliedZoneSetsTmpFileName = "FancyZonesAppliedZoneSets.json";
- private const string DeletedCustomZoneSetsTmpFileName = "FancyZonesDeletedCustomZoneSets.json";
-
private const string LayoutTypeBlankStr = "blank";
private const string NullUuidStr = "null";
- // DeviceInfo JSON tags
- private const string DeviceIdJsonTag = "device-id";
- private const string ActiveZoneSetJsonTag = "active-zoneset";
- private const string UuidJsonTag = "uuid";
- private const string TypeJsonTag = "type";
- private const string EditorShowSpacingJsonTag = "editor-show-spacing";
- private const string EditorSpacingJsonTag = "editor-spacing";
- private const string EditorZoneCountJsonTag = "editor-zone-count";
- private const string EditorSensitivityRadiusJsonTag = "editor-sensitivity-radius";
-
- private const string FocusJsonTag = "focus";
- private const string ColumnsJsonTag = "columns";
- private const string RowsJsonTag = "rows";
- private const string GridJsonTag = "grid";
- private const string PriorityGridJsonTag = "priority-grid";
- private const string CustomJsonTag = "custom";
-
- private const string DebugMode = "Debug";
-
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
private static readonly byte[][] _priorityData = new byte[][]
{
@@ -124,19 +83,8 @@ namespace FancyZonesEditor
}
}
- public Settings()
+ public MainWindowSettingsModel()
{
- string tmpDirPath = _fileSystem.Path.GetTempPath();
-
- ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName;
- AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName;
- DeletedCustomZoneSetsTmpFile = tmpDirPath + DeletedCustomZoneSetsTmpFileName;
-
- var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- FancyZonesSettingsFile = localAppDataDir + ZonesSettingsFile;
-
- ParseCommandLineArgs();
-
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
DefaultModels = new List(5);
_focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
@@ -164,7 +112,7 @@ namespace FancyZonesEditor
_blankCustomModel = new CanvasLayoutModel(Properties.Resources.Custom_Layout_Create_New, LayoutType.Blank);
- UpdateLayoutModels();
+ UpdateTemplateLayoutModels();
}
// ZoneCount - number of zones selected in the picker window
@@ -172,82 +120,79 @@ namespace FancyZonesEditor
{
get
{
- return _zoneCount;
+ return App.Overlay.CurrentLayoutSettings.ZoneCount;
}
set
{
- if (_zoneCount != value)
+ if (App.Overlay.CurrentLayoutSettings.ZoneCount != value)
{
- _zoneCount = value;
- UpdateLayoutModels();
- FirePropertyChanged();
+ App.Overlay.CurrentLayoutSettings.ZoneCount = value;
+ UpdateTemplateLayoutModels();
+ FirePropertyChanged(nameof(ZoneCount));
}
}
}
- private int _zoneCount;
-
// Spacing - how much space in between zones of the grid do you want
public int Spacing
{
get
{
- return _spacing;
+ return App.Overlay.CurrentLayoutSettings.Spacing;
}
set
{
- if (_spacing != value)
+ value = Math.Max(0, value);
+ if (App.Overlay.CurrentLayoutSettings.Spacing != value)
{
- _spacing = Math.Max(MaxNegativeSpacing, value);
- FirePropertyChanged();
+ App.Overlay.CurrentLayoutSettings.Spacing = value;
+ UpdateTemplateLayoutModels();
+ FirePropertyChanged(nameof(Spacing));
}
}
}
- private int _spacing;
-
// ShowSpacing - is the Spacing value used or ignored?
public bool ShowSpacing
{
get
{
- return _showSpacing;
+ return App.Overlay.CurrentLayoutSettings.ShowSpacing;
}
set
{
- if (_showSpacing != value)
+ if (App.Overlay.CurrentLayoutSettings.ShowSpacing != value)
{
- _showSpacing = value;
- FirePropertyChanged();
+ App.Overlay.CurrentLayoutSettings.ShowSpacing = value;
+ UpdateTemplateLayoutModels();
+ FirePropertyChanged(nameof(ShowSpacing));
}
}
}
- private bool _showSpacing;
-
// SensitivityRadius - how much space inside the zone to highlight the adjacent zone too
public int SensitivityRadius
{
get
{
- return _sensitivityRadius;
+ return App.Overlay.CurrentLayoutSettings.SensitivityRadius;
}
set
{
- if (_sensitivityRadius != value)
+ value = Math.Max(0, value);
+ if (App.Overlay.CurrentLayoutSettings.SensitivityRadius != value)
{
- _sensitivityRadius = Math.Max(0, value);
- FirePropertyChanged();
+ App.Overlay.CurrentLayoutSettings.SensitivityRadius = value;
+ UpdateTemplateLayoutModels();
+ FirePropertyChanged(nameof(SensitivityRadius));
}
}
}
- private int _sensitivityRadius;
-
// IsShiftKeyPressed - is the shift key currently being held down
public bool IsShiftKeyPressed
{
@@ -261,7 +206,7 @@ namespace FancyZonesEditor
if (_isShiftKeyPressed != value)
{
_isShiftKeyPressed = value;
- FirePropertyChanged();
+ FirePropertyChanged(nameof(IsShiftKeyPressed));
}
}
}
@@ -281,41 +226,16 @@ namespace FancyZonesEditor
if (_isCtrlKeyPressed != value)
{
_isCtrlKeyPressed = value;
- FirePropertyChanged();
+ FirePropertyChanged(nameof(IsCtrlKeyPressed));
}
}
}
private bool _isCtrlKeyPressed;
- public static Rect WorkArea { get; private set; }
-
- public static List UsedWorkAreas { get; private set; }
-
- public static string UniqueKey { get; private set; }
-
- public static string ActiveZoneSetUUid { get; private set; }
-
- public static LayoutType ActiveZoneSetLayoutType { get; private set; }
-
- public static string ActiveZoneSetTmpFile { get; private set; }
-
- public static string AppliedZoneSetTmpFile { get; private set; }
-
- public static string DeletedCustomZoneSetsTmpFile { get; private set; }
-
- public static string FancyZonesSettingsFile { get; private set; }
-
- public static int PowerToysPID
- {
- get { return _powerToysPID; }
- }
-
- private static int _powerToysPID;
-
// UpdateLayoutModels
// Update the five default layouts based on the new ZoneCount
- private void UpdateLayoutModels()
+ private void UpdateTemplateLayoutModels()
{
// Update the "Focus" Default Layout
_focusModel.Zones.Clear();
@@ -328,9 +248,13 @@ namespace FancyZonesEditor
// If changing focus layout zones size and/or increment,
// same change should be applied in ZoneSet.cpp (ZoneSet::CalculateFocusLayout)
- Int32Rect focusZoneRect = new Int32Rect(100, 100, (int)(WorkArea.Width * 0.4), (int)(WorkArea.Height * 0.4));
- int focusRectXIncrement = (ZoneCount <= 1) ? 0 : 50;
- int focusRectYIncrement = (ZoneCount <= 1) ? 0 : 50;
+ var workingArea = App.Overlay.WorkArea;
+ int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers
+ int width = (int)(workingArea.Width * 0.4);
+ int height = (int)(workingArea.Height * 0.4);
+ Int32Rect focusZoneRect = new Int32Rect(topLeftCoordinate, topLeftCoordinate, width, height);
+ int focusRectXIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50);
+ int focusRectYIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50);
for (int i = 0; i < ZoneCount; i++)
{
@@ -422,128 +346,6 @@ namespace FancyZonesEditor
}
}
- private void ParseDeviceInfoData(ParseDeviceMode mode = ParseDeviceMode.Prod)
- {
- try
- {
- string layoutType = LayoutTypeBlankStr;
- ActiveZoneSetUUid = NullUuidStr;
- JsonElement jsonObject = default(JsonElement);
-
- if (_fileSystem.File.Exists(Settings.ActiveZoneSetTmpFile))
- {
- Stream inputStream = _fileSystem.File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open);
- jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
- inputStream.Close();
- UniqueKey = jsonObject.GetProperty(DeviceIdJsonTag).GetString();
- ActiveZoneSetUUid = jsonObject.GetProperty(ActiveZoneSetJsonTag).GetProperty(UuidJsonTag).GetString();
- layoutType = jsonObject.GetProperty(ActiveZoneSetJsonTag).GetProperty(TypeJsonTag).GetString();
- }
-
- if (mode == ParseDeviceMode.Debug || ActiveZoneSetUUid == NullUuidStr || layoutType == LayoutTypeBlankStr)
- {
- // Default or there is no active layout on current device
- ActiveZoneSetLayoutType = LayoutType.Focus;
- _showSpacing = true;
- _spacing = 16;
- _zoneCount = 3;
- _sensitivityRadius = 20;
- }
- else
- {
- switch (layoutType)
- {
- case FocusJsonTag:
- ActiveZoneSetLayoutType = LayoutType.Focus;
- break;
- case ColumnsJsonTag:
- ActiveZoneSetLayoutType = LayoutType.Columns;
- break;
- case RowsJsonTag:
- ActiveZoneSetLayoutType = LayoutType.Rows;
- break;
- case GridJsonTag:
- ActiveZoneSetLayoutType = LayoutType.Grid;
- break;
- case PriorityGridJsonTag:
- ActiveZoneSetLayoutType = LayoutType.PriorityGrid;
- break;
- case CustomJsonTag:
- ActiveZoneSetLayoutType = LayoutType.Custom;
- break;
- }
-
- _showSpacing = jsonObject.GetProperty(EditorShowSpacingJsonTag).GetBoolean();
- _spacing = jsonObject.GetProperty(EditorSpacingJsonTag).GetInt32();
- _zoneCount = jsonObject.GetProperty(EditorZoneCountJsonTag).GetInt32();
- _sensitivityRadius = jsonObject.GetProperty(EditorSensitivityRadiusJsonTag).GetInt32();
- }
- }
- catch (Exception ex)
- {
- LayoutModel.ShowExceptionMessageBox(Properties.Resources.Error_Parsing_Device_Info, ex);
- }
- }
-
- private void ParseCommandLineArgs()
- {
- WorkArea = SystemParameters.WorkArea;
- UsedWorkAreas = new List { WorkArea };
-
- string[] args = Environment.GetCommandLineArgs();
-
- if (args.Length == 2)
- {
- if (args[1].Equals(DebugMode))
- {
- ParseDeviceInfoData(ParseDeviceMode.Debug);
- }
- else
- {
- MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title);
- ((App)Application.Current).Shutdown();
- }
- }
- else if (args.Length == 3)
- {
- UsedWorkAreas.Clear();
- foreach (var singleMonitorString in args[(int)CmdArgs.WorkAreaSize].Split('/'))
- {
- var parsedLocation = singleMonitorString.Split('_');
- if (parsedLocation.Length != 4)
- {
- MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title);
- ((App)Application.Current).Shutdown();
- }
-
- var x = int.Parse(parsedLocation[(int)WorkAreaCmdArgElements.X]);
- var y = int.Parse(parsedLocation[(int)WorkAreaCmdArgElements.Y]);
- var width = int.Parse(parsedLocation[(int)WorkAreaCmdArgElements.Width]);
- var height = int.Parse(parsedLocation[(int)WorkAreaCmdArgElements.Height]);
-
- Rect thisMonitor = new Rect(x, y, width, height);
- if (UsedWorkAreas.Count == 0)
- {
- WorkArea = thisMonitor;
- }
- else
- {
- WorkArea = Rect.Union(WorkArea, thisMonitor);
- }
-
- UsedWorkAreas.Add(thisMonitor);
- }
-
- int.TryParse(args[(int)CmdArgs.PowerToysPID], out _powerToysPID);
- ParseDeviceInfoData();
- }
- else
- {
- MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title);
- ((App)Application.Current).Shutdown();
- }
- }
-
public IList DefaultModels { get; }
public static ObservableCollection CustomModels
@@ -567,6 +369,120 @@ namespace FancyZonesEditor
return model.Type != LayoutType.Custom;
}
+ public LayoutModel UpdateSelectedLayoutModel()
+ {
+ UpdateTemplateLayoutModels();
+ ResetAppliedModel();
+ ResetSelectedModel();
+
+ LayoutModel foundModel = null;
+ LayoutSettings currentApplied = App.Overlay.CurrentLayoutSettings;
+
+ // set new layout
+ if (currentApplied.Type == LayoutType.Custom)
+ {
+ foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
+ {
+ if ("{" + model.Guid.ToString().ToUpper() + "}" == currentApplied.ZonesetUuid.ToUpper())
+ {
+ // found match
+ foundModel = model;
+ break;
+ }
+ }
+ }
+ else
+ {
+ foreach (LayoutModel model in DefaultModels)
+ {
+ if (model.Type == currentApplied.Type)
+ {
+ // found match
+ foundModel = model;
+ break;
+ }
+ }
+ }
+
+ if (foundModel == null)
+ {
+ foundModel = DefaultModels[0];
+ }
+
+ foundModel.IsSelected = true;
+ foundModel.IsApplied = true;
+
+ FirePropertyChanged(nameof(IsCustomLayoutActive));
+ return foundModel;
+ }
+
+ public void ResetSelectedModel()
+ {
+ foreach (LayoutModel model in CustomModels)
+ {
+ if (model.IsSelected)
+ {
+ model.IsSelected = false;
+ break;
+ }
+ }
+
+ foreach (LayoutModel model in DefaultModels)
+ {
+ if (model.IsSelected)
+ {
+ model.IsSelected = false;
+ break;
+ }
+ }
+ }
+
+ public void ResetAppliedModel()
+ {
+ foreach (LayoutModel model in CustomModels)
+ {
+ if (model.IsApplied)
+ {
+ model.IsApplied = false;
+ break;
+ }
+ }
+
+ foreach (LayoutModel model in DefaultModels)
+ {
+ if (model.IsApplied)
+ {
+ model.IsApplied = false;
+ break;
+ }
+ }
+ }
+
+ public void UpdateDesktopDependantProperties(LayoutSettings prevSettings)
+ {
+ UpdateTemplateLayoutModels();
+
+ if (prevSettings.ZoneCount != ZoneCount)
+ {
+ FirePropertyChanged(nameof(ZoneCount));
+ }
+
+ if (prevSettings.Spacing != Spacing)
+ {
+ FirePropertyChanged(nameof(Spacing));
+ }
+
+ if (prevSettings.ShowSpacing != ShowSpacing)
+ {
+ FirePropertyChanged(nameof(ShowSpacing));
+ }
+
+ if (prevSettings.SensitivityRadius != SensitivityRadius)
+ {
+ FirePropertyChanged(nameof(SensitivityRadius));
+ }
+ }
+
// implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs
new file mode 100644
index 0000000000..3dd584aae0
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/Monitor.cs
@@ -0,0 +1,58 @@
+// 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.Reflection;
+using System.Windows;
+using System.Windows.Media;
+using FancyZonesEditor.Utils;
+
+namespace FancyZonesEditor.Models
+{
+ public class Monitor
+ {
+ public LayoutOverlayWindow Window { get; private set; }
+
+ public LayoutSettings Settings { get; set; }
+
+ public Device Device { get; set; }
+
+ public Monitor(Rect bounds, Rect workArea, bool primary)
+ {
+ Window = new LayoutOverlayWindow();
+ Settings = new LayoutSettings();
+ Device = new Device(bounds, workArea, primary);
+
+ if (App.DebugMode)
+ {
+ long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ PropertyInfo[] properties = typeof(Brushes).GetProperties();
+ Window.Opacity = 0.5;
+ Window.Background = (Brush)properties[milliseconds % properties.Length].GetValue(null, null);
+ }
+
+ Window.Left = workArea.X;
+ Window.Top = workArea.Y;
+ Window.Width = workArea.Width;
+ Window.Height = workArea.Height;
+ }
+
+ public Monitor(string id, int dpi, Rect bounds, Rect workArea, bool primary)
+ : this(bounds, workArea, primary)
+ {
+ Device = new Device(id, dpi, bounds, workArea, primary);
+ }
+
+ public void Scale(double scaleFactor)
+ {
+ Device.Scale(scaleFactor);
+
+ var workArea = Device.WorkAreaRect;
+ Window.Left = workArea.X;
+ Window.Top = workArea.Y;
+ Window.Width = workArea.Width;
+ Window.Height = workArea.Height;
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MonitorInfoModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MonitorInfoModel.cs
new file mode 100644
index 0000000000..3a6a6f60af
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MonitorInfoModel.cs
@@ -0,0 +1,86 @@
+// 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.ComponentModel;
+using FancyZonesEditor.ViewModels;
+
+namespace FancyZonesEditor.Utils
+{
+ public class MonitorInfoModel : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public MonitorInfoModel(int index, int height, int width, int dpi, bool selected = false)
+ {
+ Index = index;
+ ScreenBoundsHeight = height;
+ ScreenBoundsWidth = width;
+ DPI = dpi;
+ Selected = selected;
+ }
+
+ public int Index { get; set; }
+
+ public int ScreenBoundsHeight { get; set; }
+
+ public double DisplayHeight
+ {
+ get
+ {
+ return ScreenBoundsHeight * MonitorViewModel.DesktopPreviewMultiplier;
+ }
+ }
+
+ public int ScreenBoundsWidth { get; set; }
+
+ public double DisplayWidth
+ {
+ get
+ {
+ return ScreenBoundsWidth * MonitorViewModel.DesktopPreviewMultiplier;
+ }
+ }
+
+ public int DPI { get; set; }
+
+ public string Dimensions
+ {
+ get
+ {
+ if (App.DebugMode)
+ {
+ var rect = App.Overlay.Monitors[Index - 1].Device.WorkAreaRect;
+ return "Screen: (" + rect.X + ", " + rect.Y + "); (" + rect.Width + ", " + rect.Height + ")";
+ }
+ else
+ {
+ return ScreenBoundsWidth + " x " + ScreenBoundsHeight;
+ }
+ }
+ }
+
+ public bool Selected
+ {
+ get
+ {
+ return _selected;
+ }
+
+ set
+ {
+ if (_selected == value)
+ {
+ return;
+ }
+
+ _selected = value;
+ OnPropertyChanged(nameof(Selected));
+ }
+ }
+
+ private bool _selected;
+
+ protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs
new file mode 100644
index 0000000000..d61bf85499
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs
@@ -0,0 +1,397 @@
+// 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;
+using System.Windows.Controls;
+using FancyZonesEditor.Models;
+
+namespace FancyZonesEditor
+{
+ public class Overlay
+ {
+ private MainWindow _mainWindow;
+
+ private LayoutPreview _layoutPreview;
+ private UserControl _editor;
+
+ public List Monitors { get; private set; }
+
+ public Rect WorkArea
+ {
+ get
+ {
+ if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count)
+ {
+ return Monitors[CurrentDesktop].Device.WorkAreaRect;
+ }
+
+ return default(Rect);
+ }
+ }
+
+ public LayoutSettings CurrentLayoutSettings
+ {
+ get
+ {
+ if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count)
+ {
+ return Monitors[CurrentDesktop].Settings;
+ }
+
+ return new LayoutSettings();
+ }
+ }
+
+ public Window CurrentLayoutWindow
+ {
+ get
+ {
+ if (Monitors.Count > 0 && CurrentDesktop < Monitors.Count)
+ {
+ return Monitors[CurrentDesktop].Window;
+ }
+
+ return default(Window);
+ }
+ }
+
+ public List WorkAreas { get; private set; }
+
+ public object CurrentDataContext
+ {
+ get
+ {
+ return _dataContext;
+ }
+
+ set
+ {
+ _dataContext = value;
+ CurrentLayoutWindow.DataContext = value;
+ }
+ }
+
+ private object _dataContext;
+
+ public int DesktopsCount
+ {
+ get
+ {
+ return Monitors.Count;
+ }
+ }
+
+ public int CurrentDesktop
+ {
+ get
+ {
+ return _currentDesktop;
+ }
+
+ set
+ {
+ if (value != _currentDesktop)
+ {
+ if (value < 0 || value >= DesktopsCount)
+ {
+ return;
+ }
+
+ var prevSettings = CurrentLayoutSettings;
+ _currentDesktop = value;
+
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
+ if (settings != null)
+ {
+ settings.ResetAppliedModel();
+ settings.UpdateDesktopDependantProperties(prevSettings);
+ }
+
+ Update();
+ }
+ }
+ }
+
+ private int _currentDesktop = 0;
+
+ public bool SpanZonesAcrossMonitors
+ {
+ get
+ {
+ return _spanZonesAcrossMonitors;
+ }
+
+ set
+ {
+ _spanZonesAcrossMonitors = value;
+
+ if (_spanZonesAcrossMonitors)
+ {
+ Rect workArea = default(Rect);
+ Rect bounds = default(Rect);
+
+ foreach (Monitor monitor in Monitors)
+ {
+ workArea = Rect.Union(workArea, monitor.Device.WorkAreaRect);
+ bounds = Rect.Union(bounds, monitor.Device.ScaledBounds);
+ }
+
+ Monitors.Clear();
+ Monitors.Add(new Monitor(bounds, workArea, true));
+ }
+ }
+ }
+
+ private bool _spanZonesAcrossMonitors;
+
+ public bool MultiMonitorMode
+ {
+ get
+ {
+ return DesktopsCount > 1 && !SpanZonesAcrossMonitors;
+ }
+ }
+
+ public Overlay()
+ {
+ WorkAreas = new List();
+ Monitors = new List();
+
+ var screens = System.Windows.Forms.Screen.AllScreens;
+ foreach (System.Windows.Forms.Screen screen in screens)
+ {
+ Rect bounds = new Rect(screen.Bounds.X, screen.Bounds.Y, screen.Bounds.Width, screen.Bounds.Height);
+ Rect workArea = new Rect(screen.WorkingArea.X, screen.WorkingArea.Y, screen.WorkingArea.Width, screen.WorkingArea.Height);
+ Add(bounds, workArea, screen.Primary);
+ }
+ }
+
+ public void Show()
+ {
+ _layoutPreview = new LayoutPreview
+ {
+ IsActualSize = true,
+ Opacity = 0.5,
+ };
+
+ ShowLayout();
+ OpenMainWindow();
+ }
+
+ public void ShowLayout()
+ {
+ MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
+ CurrentDataContext = settings.UpdateSelectedLayoutModel();
+
+ var window = CurrentLayoutWindow;
+ window.Content = _layoutPreview;
+ window.DataContext = CurrentDataContext;
+
+ if (_layoutPreview != null)
+ {
+ _layoutPreview.UpdatePreview();
+ }
+
+ for (int i = 0; i < DesktopsCount; i++)
+ {
+ Monitors[i].Window.Show();
+ }
+ }
+
+ public void OpenEditor(LayoutModel model)
+ {
+ _layoutPreview = null;
+ if (CurrentDataContext is GridLayoutModel)
+ {
+ _editor = new GridEditor();
+ }
+ else if (CurrentDataContext is CanvasLayoutModel)
+ {
+ _editor = new CanvasEditor();
+ }
+
+ CurrentLayoutWindow.Content = _editor;
+
+ EditorWindow window;
+ bool isGrid = false;
+ if (model is GridLayoutModel)
+ {
+ window = new GridEditorWindow();
+ isGrid = true;
+ }
+ else
+ {
+ window = new CanvasEditorWindow();
+ }
+
+ window.Owner = Monitors[App.Overlay.CurrentDesktop].Window;
+ window.DataContext = model;
+ window.Show();
+
+ if (isGrid)
+ {
+ (window as GridEditorWindow).NameTextBox().Focus();
+ }
+
+ window.LeftWindowCommands = null;
+ window.RightWindowCommands = null;
+ }
+
+ public void CloseEditor()
+ {
+ _editor = null;
+ _layoutPreview = new LayoutPreview
+ {
+ IsActualSize = true,
+ Opacity = 0.5,
+ };
+
+ CurrentLayoutWindow.Content = _layoutPreview;
+
+ OpenMainWindow();
+ }
+
+ public void CloseLayoutWindow()
+ {
+ for (int i = 0; i < DesktopsCount; i++)
+ {
+ Monitors[i].Window.Close();
+ }
+ }
+
+ public double ScaleCoordinateWithCurrentMonitorDpi(double coordinate)
+ {
+ if (Monitors.Count == 0)
+ {
+ return coordinate;
+ }
+
+ double minimalDpi = Monitors[0].Device.Dpi;
+ foreach (Monitor monitor in Monitors)
+ {
+ if (minimalDpi > monitor.Device.Dpi)
+ {
+ minimalDpi = monitor.Device.Dpi;
+ }
+ }
+
+ if (minimalDpi == 0 || Monitors[CurrentDesktop].Device.Dpi == 0)
+ {
+ return coordinate;
+ }
+
+ double scaleFactor = minimalDpi / Monitors[CurrentDesktop].Device.Dpi;
+ return Math.Round(coordinate * scaleFactor);
+ }
+
+ private void Update()
+ {
+ CloseLayout();
+
+ if (_mainWindow != null)
+ {
+ _mainWindow.Update();
+ }
+
+ ShowLayout();
+ }
+
+ private void CloseLayout()
+ {
+ var window = CurrentLayoutWindow;
+ window.Content = null;
+ window.DataContext = null;
+ }
+
+ private void OpenMainWindow()
+ {
+ if (_mainWindow == null)
+ {
+ _mainWindow = new MainWindow(SpanZonesAcrossMonitors, WorkArea);
+ }
+
+ // reset main window owner to keep it on the top
+ _mainWindow.Owner = CurrentLayoutWindow;
+ _mainWindow.ShowActivated = true;
+ _mainWindow.Topmost = true;
+ _mainWindow.Show();
+ _mainWindow.LeftWindowCommands = null;
+ _mainWindow.RightWindowCommands = null;
+
+ // window is set to topmost to make sure it shows on top of PowerToys settings page
+ // we can reset topmost flag now
+ _mainWindow.Topmost = false;
+ }
+
+ private void Add(Rect bounds, Rect workArea, bool primary)
+ {
+ var monitor = new Monitor(bounds, workArea, primary);
+
+ bool inserted = false;
+ var workAreaRect = workArea;
+ for (int i = 0; i < Monitors.Count && !inserted; i++)
+ {
+ var rect = Monitors[i].Device.WorkAreaRect;
+ if (workAreaRect.Left < rect.Left && (workAreaRect.Top <= rect.Top || workAreaRect.Top == 0))
+ {
+ Monitors.Insert(i, monitor);
+ inserted = true;
+ }
+ else if (workAreaRect.Left == rect.Left && workAreaRect.Top < rect.Top)
+ {
+ Monitors.Insert(i, monitor);
+ inserted = true;
+ }
+ }
+
+ if (!inserted)
+ {
+ 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;
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs
index 8e8f3d2621..301b529106 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs
@@ -105,6 +105,15 @@ namespace FancyZonesEditor.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Close.
+ ///
+ public static string Close {
+ get {
+ return ResourceManager.GetString("Close", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Error logged to .
///
@@ -186,6 +195,24 @@ namespace FancyZonesEditor.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Error applying layout.
+ ///
+ public static string Error_Applying_Layout {
+ get {
+ return ResourceManager.GetString("Error_Applying_Layout", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to FancyZones Editor Exception Handler.
+ ///
+ public static string Error_Exception_Message_Box_Title {
+ get {
+ return ResourceManager.GetString("Error_Exception_Message_Box_Title", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to FancyZones Editor arguments are invalid..
///
@@ -195,6 +222,24 @@ namespace FancyZonesEditor.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Layout '{0}' has malformed data.
+ ///
+ public static string Error_Layout_Malformed_Data {
+ get {
+ return ResourceManager.GetString("Error_Layout_Malformed_Data", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Error loading custom layouts.
+ ///
+ public static string Error_Loading_Custom_Layouts {
+ get {
+ return ResourceManager.GetString("Error_Loading_Custom_Layouts", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to FancyZones Editor Error.
///
@@ -204,6 +249,15 @@ namespace FancyZonesEditor.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Match not found ({0}).
+ ///
+ public static string Error_Monitor_Match_Not_Found {
+ get {
+ return ResourceManager.GetString("Error_Monitor_Match_Not_Found", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to FancyZones Editor should not be run as standalone application..
///
@@ -222,6 +276,33 @@ namespace FancyZonesEditor.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Error persisting custom layout.
+ ///
+ public static string Error_Persisting_Custom_Layout {
+ get {
+ return ResourceManager.GetString("Error_Persisting_Custom_Layout", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Please report the bug to .
+ ///
+ public static string Error_Report {
+ get {
+ return ResourceManager.GetString("Error_Report", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Error serializing deleted layouts.
+ ///
+ public static string Error_Serializing_Deleted_Layouts {
+ get {
+ return ResourceManager.GetString("Error_Serializing_Deleted_Layouts", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to FancyZones Editor.
///
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx
index c95348bbb1..52be380f27 100644
--- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx
@@ -226,4 +226,31 @@
Templates tab selected, press ctrl + tab to switch to Custom
+
+ Close
+
+
+ Error applying layout
+
+
+ Layout '{0}' has malformed data
+
+
+ Error loading custom layouts
+
+
+ Error persisting custom layout
+
+
+ Error serializing deleted layouts
+
+
+ FancyZones Editor Exception Handler
+
+
+ Please report the bug to
+
+
+ Match not found ({0})
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventArgs`1.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventArgs`1.cs
new file mode 100644
index 0000000000..eeea75476e
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventArgs`1.cs
@@ -0,0 +1,18 @@
+// 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;
+
+namespace FancyZonesEditor.Utils
+{
+ public class EventArgs : EventArgs
+ {
+ public EventArgs(T value)
+ {
+ Value = value;
+ }
+
+ public T Value { get; private set; }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventRaiser.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventRaiser.cs
new file mode 100644
index 0000000000..6cdce5f35d
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/EventRaiser.cs
@@ -0,0 +1,32 @@
+// 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;
+
+namespace FancyZonesEditor.Utils
+{
+ public static class EventRaiser
+ {
+ public static void Raise(this EventHandler handler, object sender)
+ {
+ handler?.Invoke(sender, EventArgs.Empty);
+ }
+
+ public static void Raise(this EventHandler> handler, object sender, T value)
+ {
+ handler?.Invoke(sender, new EventArgs(value));
+ }
+
+ public static void Raise(this EventHandler handler, object sender, T value)
+ where T : EventArgs
+ {
+ handler?.Invoke(sender, value);
+ }
+
+ public static void Raise(this EventHandler> handler, object sender, EventArgs value)
+ {
+ handler?.Invoke(sender, value);
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs
new file mode 100644
index 0000000000..a34886565a
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs
@@ -0,0 +1,678 @@
+// 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.Collections.ObjectModel;
+using System.IO;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Windows;
+using FancyZonesEditor.Models;
+
+namespace FancyZonesEditor.Utils
+{
+ public class FancyZonesEditorIO
+ {
+ // Non-localizable strings: JSON tags
+ private const string AppliedZonesetsJsonTag = "applied-zonesets";
+ private const string DeviceIdJsonTag = "device-id";
+ private const string ActiveZoneSetJsonTag = "active-zoneset";
+ private const string UuidJsonTag = "uuid";
+ private const string TypeJsonTag = "type";
+ private const string EditorShowSpacingJsonTag = "editor-show-spacing";
+ private const string EditorSpacingJsonTag = "editor-spacing";
+ private const string EditorZoneCountJsonTag = "editor-zone-count";
+ private const string EditorSensitivityRadiusJsonTag = "editor-sensitivity-radius";
+
+ private const string FocusJsonTag = "focus";
+ private const string ColumnsJsonTag = "columns";
+ private const string RowsJsonTag = "rows";
+ private const string GridJsonTag = "grid";
+ private const string PriorityGridJsonTag = "priority-grid";
+ private const string CustomJsonTag = "custom";
+
+ private const string NameJsonTag = "name";
+ private const string CustomZoneSetsJsonTag = "custom-zone-sets";
+ private const string InfoJsonTag = "info";
+ private const string RowsPercentageJsonTag = "rows-percentage";
+ private const string ColumnsPercentageJsonTag = "columns-percentage";
+ private const string CellChildMapJsonTag = "cell-child-map";
+ private const string ZonesJsonTag = "zones";
+ private const string CanvasJsonTag = "canvas";
+ private const string RefWidthJsonTag = "ref-width";
+ private const string RefHeightJsonTag = "ref-height";
+ private const string XJsonTag = "X";
+ private const string YJsonTag = "Y";
+ private const string WidthJsonTag = "width";
+ private const string HeightJsonTag = "height";
+
+ // Non-localizable strings: Files
+ private const string ZonesSettingsFile = "\\Microsoft\\PowerToys\\FancyZones\\zones-settings.json";
+ private const string ActiveZoneSetsTmpFileName = "FancyZonesActiveZoneSets.json";
+ private const string AppliedZoneSetsTmpFileName = "FancyZonesAppliedZoneSets.json";
+ private const string DeletedCustomZoneSetsTmpFileName = "FancyZonesDeletedCustomZoneSets.json";
+
+ private readonly IFileSystem _fileSystem = new FileSystem();
+
+ private JsonSerializerOptions _options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = new DashCaseNamingPolicy(),
+ };
+
+ public string ActiveZoneSetTmpFile { get; private set; }
+
+ public string AppliedZoneSetTmpFile { get; private set; }
+
+ public string DeletedCustomZoneSetsTmpFile { get; private set; }
+
+ public string FancyZonesSettingsFile { get; private set; }
+
+ private enum CmdArgs
+ {
+ PowerToysPID = 0,
+ SpanZones,
+ TargetMonitorId,
+ MonitorsCount,
+ MonitorId,
+ DPI,
+ MonitorLeft,
+ MonitorTop,
+ }
+
+ private struct NativeMonitorData
+ {
+ public string Id { get; set; }
+
+ public int Dpi { get; set; }
+
+ public int X { get; set; }
+
+ public int Y { get; set; }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.Append("ID: ");
+ sb.AppendLine(Id);
+ sb.Append("DPI: ");
+ sb.AppendLine(Dpi.ToString());
+
+ sb.Append("X: ");
+ sb.AppendLine(X.ToString());
+ sb.Append("Y: ");
+ sb.AppendLine(Y.ToString());
+
+ return sb.ToString();
+ }
+ }
+
+ private struct ActiveZoneSetWrapper
+ {
+ public string Uuid { get; set; }
+
+ public string Type { get; set; }
+ }
+
+ private struct AppliedZoneSet
+ {
+ public string DeviceId { get; set; }
+
+ public ActiveZoneSetWrapper ActiveZoneset { get; set; }
+
+ public bool EditorShowSpacing { get; set; }
+
+ public int EditorSpacing { get; set; }
+
+ public int EditorZoneCount { get; set; }
+
+ public int EditorSensitivityRadius { get; set; }
+ }
+
+ private struct AppliedZonesetsToDesktops
+ {
+ public List AppliedZonesets { get; set; }
+ }
+
+ private struct DeletedCustomZoneSetsWrapper
+ {
+ public List DeletedCustomZoneSets { get; set; }
+ }
+
+ private struct CreatedCustomZoneSetsWrapper
+ {
+ public List CreatedCustomZoneSets { get; set; }
+ }
+
+ public FancyZonesEditorIO()
+ {
+ string tmpDirPath = _fileSystem.Path.GetTempPath();
+
+ ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName;
+ AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName;
+ DeletedCustomZoneSetsTmpFile = tmpDirPath + DeletedCustomZoneSetsTmpFileName;
+
+ var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ FancyZonesSettingsFile = localAppDataDir + ZonesSettingsFile;
+ }
+
+ // All strings in this function shouldn't be localized.
+ public static void ParseCommandLineArguments()
+ {
+ string[] args = Environment.GetCommandLineArgs();
+
+ if (args.Length < 2 && !App.DebugMode)
+ {
+ MessageBox.Show(Properties.Resources.Error_Not_Standalone_App, Properties.Resources.Error_Message_Box_Title);
+ ((App)Application.Current).Shutdown();
+ }
+
+ try
+ {
+ /*
+ * Divider: /
+ * Parts:
+ * (1) Process id
+ * (2) Span zones across monitors
+ * (3) Monitor id where the Editor should be opened
+ * (4) Monitors count
+ *
+ * Data for each monitor:
+ * (5) Monitor id
+ * (6) DPI
+ * (7) monitor left
+ * (8) monitor top
+ * ...
+ */
+ var argsParts = args[1].Split('/');
+
+ // Process ID
+ App.PowerToysPID = int.Parse(argsParts[(int)CmdArgs.PowerToysPID]);
+
+ // Span zones across monitors
+ App.Overlay.SpanZonesAcrossMonitors = int.Parse(argsParts[(int)CmdArgs.SpanZones]) == 1;
+
+ if (!App.Overlay.SpanZonesAcrossMonitors)
+ {
+ // Target monitor id
+ string targetMonitorName = argsParts[(int)CmdArgs.TargetMonitorId];
+
+ // Monitors count
+ int count = int.Parse(argsParts[(int)CmdArgs.MonitorsCount]);
+ if (count != App.Overlay.DesktopsCount)
+ {
+ MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title);
+ ((App)Application.Current).Shutdown();
+ }
+
+ double primaryMonitorDPI = 96f;
+ double minimalUsedMonitorDPI = double.MaxValue;
+
+ // Parse the native monitor data
+ List nativeMonitorData = new List();
+ const int monitorArgsCount = 4;
+ for (int i = 0; i < count; i++)
+ {
+ var nativeData = default(NativeMonitorData);
+ nativeData.Id = argsParts[(int)CmdArgs.MonitorId + (i * monitorArgsCount)];
+ nativeData.Dpi = int.Parse(argsParts[(int)CmdArgs.DPI + (i * monitorArgsCount)]);
+ nativeData.X = int.Parse(argsParts[(int)CmdArgs.MonitorLeft + (i * monitorArgsCount)]);
+ nativeData.Y = int.Parse(argsParts[(int)CmdArgs.MonitorTop + (i * monitorArgsCount)]);
+ nativeMonitorData.Add(nativeData);
+
+ if (nativeData.X == 0 && nativeData.Y == 0)
+ {
+ primaryMonitorDPI = nativeData.Dpi;
+ }
+
+ if (minimalUsedMonitorDPI > nativeData.Dpi)
+ {
+ minimalUsedMonitorDPI = nativeData.Dpi;
+ }
+ }
+
+ var monitors = App.Overlay.Monitors;
+ double identifyScaleFactor = minimalUsedMonitorDPI / primaryMonitorDPI;
+ double scaleFactor = 96f / primaryMonitorDPI;
+
+ // Update monitors data
+ foreach (Monitor monitor in monitors)
+ {
+ bool matchFound = false;
+ monitor.Scale(scaleFactor);
+
+ double scaledBoundX = (int)(monitor.Device.UnscaledBounds.X * identifyScaleFactor);
+ double scaledBoundY = (int)(monitor.Device.UnscaledBounds.Y * identifyScaleFactor);
+
+ foreach (NativeMonitorData nativeData in nativeMonitorData)
+ {
+ // Can't do an exact match since the rounding algorithm used by the framework is different from ours
+ if (scaledBoundX >= (nativeData.X - 1) && scaledBoundX <= (nativeData.X + 1) &&
+ scaledBoundY >= (nativeData.Y - 1) && scaledBoundY <= (nativeData.Y + 1))
+ {
+ monitor.Device.Id = nativeData.Id;
+ monitor.Device.Dpi = nativeData.Dpi;
+ matchFound = true;
+ break;
+ }
+ }
+
+ if (matchFound == false)
+ {
+ MessageBox.Show(string.Format(Properties.Resources.Error_Monitor_Match_Not_Found, monitor.Device.UnscaledBounds.ToString()));
+ }
+ }
+
+ // Set active desktop
+ for (int i = 0; i < monitors.Count; i++)
+ {
+ var monitor = monitors[i];
+ if (monitor.Device.Id == targetMonitorName)
+ {
+ App.Overlay.CurrentDesktop = i;
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ MessageBox.Show(Properties.Resources.Error_Invalid_Arguments, Properties.Resources.Error_Message_Box_Title);
+ ((App)Application.Current).Shutdown();
+ }
+ }
+
+ public void ParseDeviceInfoData()
+ {
+ try
+ {
+ JsonElement jsonObject = default(JsonElement);
+
+ if (_fileSystem.File.Exists(ActiveZoneSetTmpFile))
+ {
+ Stream inputStream = _fileSystem.File.Open(ActiveZoneSetTmpFile, FileMode.Open);
+ jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
+ inputStream.Close();
+
+ JsonElement json = jsonObject.GetProperty(AppliedZonesetsJsonTag);
+
+ int layoutId = 0;
+ for (int i = 0; i < json.GetArrayLength() && layoutId < App.Overlay.DesktopsCount; i++)
+ {
+ var zonesetData = json[i];
+
+ string deviceId = zonesetData.GetProperty(DeviceIdJsonTag).GetString();
+
+ string currentLayoutType = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(TypeJsonTag).GetString();
+ LayoutType type = JsonTagToLayoutType(currentLayoutType);
+
+ if (!App.Overlay.SpanZonesAcrossMonitors)
+ {
+ var monitors = App.Overlay.Monitors;
+ for (int monitorIndex = 0; monitorIndex < monitors.Count; monitorIndex++)
+ {
+ if (monitors[monitorIndex].Device.Id == deviceId)
+ {
+ monitors[monitorIndex].Settings = new LayoutSettings
+ {
+ DeviceId = deviceId,
+ ZonesetUuid = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(UuidJsonTag).GetString(),
+ ShowSpacing = zonesetData.GetProperty(EditorShowSpacingJsonTag).GetBoolean(),
+ Spacing = zonesetData.GetProperty(EditorSpacingJsonTag).GetInt32(),
+ Type = type,
+ ZoneCount = zonesetData.GetProperty(EditorZoneCountJsonTag).GetInt32(),
+ SensitivityRadius = zonesetData.GetProperty(EditorSensitivityRadiusJsonTag).GetInt32(),
+ };
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ bool isLayoutMultiMonitor = deviceId.StartsWith("FancyZones#MultiMonitorDevice");
+ if (isLayoutMultiMonitor)
+ {
+ // one zoneset for all desktops
+ App.Overlay.Monitors[App.Overlay.CurrentDesktop].Settings = new LayoutSettings
+ {
+ DeviceId = deviceId,
+ ZonesetUuid = zonesetData.GetProperty(ActiveZoneSetJsonTag).GetProperty(UuidJsonTag).GetString(),
+ ShowSpacing = zonesetData.GetProperty(EditorShowSpacingJsonTag).GetBoolean(),
+ Spacing = zonesetData.GetProperty(EditorSpacingJsonTag).GetInt32(),
+ Type = type,
+ ZoneCount = zonesetData.GetProperty(EditorZoneCountJsonTag).GetInt32(),
+ SensitivityRadius = zonesetData.GetProperty(EditorSensitivityRadiusJsonTag).GetInt32(),
+ };
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ App.ShowExceptionMessageBox(Properties.Resources.Error_Parsing_Device_Info, ex);
+ }
+ }
+
+ public void ParseLayouts(ref ObservableCollection custom, ref List deleted)
+ {
+ try
+ {
+ Stream inputStream = _fileSystem.File.Open(FancyZonesSettingsFile, FileMode.Open);
+ JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default);
+ JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty(CustomZoneSetsJsonTag).EnumerateArray();
+
+ while (customZoneSetsEnumerator.MoveNext())
+ {
+ var current = customZoneSetsEnumerator.Current;
+ string name = current.GetProperty(NameJsonTag).GetString();
+ string type = current.GetProperty(TypeJsonTag).GetString();
+ string uuid = current.GetProperty(UuidJsonTag).GetString();
+ var info = current.GetProperty(InfoJsonTag);
+
+ if (type.Equals(GridJsonTag))
+ {
+ bool error = false;
+
+ int rows = info.GetProperty(RowsJsonTag).GetInt32();
+ int columns = info.GetProperty(ColumnsJsonTag).GetInt32();
+
+ List rowsPercentage = new List(rows);
+ JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty(RowsPercentageJsonTag).EnumerateArray();
+
+ List columnsPercentage = new List(columns);
+ JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty(ColumnsPercentageJsonTag).EnumerateArray();
+
+ if (rows <= 0 || columns <= 0 || rowsPercentageEnumerator.Count() != rows || columnsPercentageEnumerator.Count() != columns)
+ {
+ error = true;
+ }
+
+ while (!error && rowsPercentageEnumerator.MoveNext())
+ {
+ int percentage = rowsPercentageEnumerator.Current.GetInt32();
+ if (percentage <= 0)
+ {
+ error = true;
+ break;
+ }
+
+ rowsPercentage.Add(percentage);
+ }
+
+ while (!error && columnsPercentageEnumerator.MoveNext())
+ {
+ int percentage = columnsPercentageEnumerator.Current.GetInt32();
+ if (percentage <= 0)
+ {
+ error = true;
+ break;
+ }
+
+ columnsPercentage.Add(percentage);
+ }
+
+ int i = 0;
+ JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty(CellChildMapJsonTag).EnumerateArray();
+ int[,] cellChildMap = new int[rows, columns];
+
+ if (cellChildMapRows.Count() != rows)
+ {
+ error = true;
+ }
+
+ while (!error && cellChildMapRows.MoveNext())
+ {
+ int j = 0;
+ JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray();
+ if (cellChildMapRowElems.Count() != columns)
+ {
+ error = true;
+ break;
+ }
+
+ while (cellChildMapRowElems.MoveNext())
+ {
+ cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32();
+ }
+
+ i++;
+ }
+
+ if (error)
+ {
+ App.ShowExceptionMessageBox(string.Format(Properties.Resources.Error_Layout_Malformed_Data, name));
+ deleted.Add(Guid.Parse(uuid).ToString().ToUpper());
+ continue;
+ }
+
+ custom.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap));
+ }
+ else if (type.Equals(CanvasJsonTag))
+ {
+ int workAreaWidth = info.GetProperty(RefWidthJsonTag).GetInt32();
+ int workAreaHeight = info.GetProperty(RefHeightJsonTag).GetInt32();
+
+ JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty(ZonesJsonTag).EnumerateArray();
+ IList zones = new List();
+
+ bool error = false;
+
+ if (workAreaWidth <= 0 || workAreaHeight <= 0)
+ {
+ error = true;
+ }
+
+ while (!error && zonesEnumerator.MoveNext())
+ {
+ int x = zonesEnumerator.Current.GetProperty(XJsonTag).GetInt32();
+ int y = zonesEnumerator.Current.GetProperty(YJsonTag).GetInt32();
+ int width = zonesEnumerator.Current.GetProperty(WidthJsonTag).GetInt32();
+ int height = zonesEnumerator.Current.GetProperty(HeightJsonTag).GetInt32();
+
+ if (width <= 0 || height <= 0)
+ {
+ error = true;
+ break;
+ }
+
+ zones.Add(new Int32Rect(x, y, width, height));
+ }
+
+ if (error)
+ {
+ App.ShowExceptionMessageBox(string.Format(Properties.Resources.Error_Layout_Malformed_Data, name));
+ deleted.Add(Guid.Parse(uuid).ToString().ToUpper());
+ continue;
+ }
+
+ custom.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, zones, workAreaWidth, workAreaHeight));
+ }
+ }
+
+ inputStream.Close();
+ }
+ catch (Exception ex)
+ {
+ App.ShowExceptionMessageBox(Properties.Resources.Error_Loading_Custom_Layouts, ex);
+ }
+ }
+
+ public void SerializeAppliedLayouts()
+ {
+ AppliedZonesetsToDesktops applied = new AppliedZonesetsToDesktops { };
+ applied.AppliedZonesets = new List();
+
+ foreach (var monitor in App.Overlay.Monitors)
+ {
+ LayoutSettings zoneset = monitor.Settings;
+ if (zoneset.ZonesetUuid.Length == 0)
+ {
+ continue;
+ }
+
+ ActiveZoneSetWrapper activeZoneSet = new ActiveZoneSetWrapper
+ {
+ Uuid = zoneset.ZonesetUuid,
+ };
+
+ activeZoneSet.Type = LayoutTypeToJsonTag(zoneset.Type);
+
+ applied.AppliedZonesets.Add(new AppliedZoneSet
+ {
+ DeviceId = zoneset.DeviceId,
+ ActiveZoneset = activeZoneSet,
+ EditorShowSpacing = zoneset.ShowSpacing,
+ EditorSpacing = zoneset.Spacing,
+ EditorZoneCount = zoneset.ZoneCount,
+ EditorSensitivityRadius = zoneset.SensitivityRadius,
+ });
+ }
+
+ try
+ {
+ string jsonString = JsonSerializer.Serialize(applied, _options);
+ _fileSystem.File.WriteAllText(ActiveZoneSetTmpFile, jsonString);
+ }
+ catch (Exception ex)
+ {
+ App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex);
+ }
+ }
+
+ public void SerializeDeletedCustomZoneSets(List models)
+ {
+ DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper
+ {
+ DeletedCustomZoneSets = models,
+ };
+
+ try
+ {
+ string jsonString = JsonSerializer.Serialize(deletedLayouts, _options);
+ _fileSystem.File.WriteAllText(DeletedCustomZoneSetsTmpFile, jsonString);
+ }
+ catch (Exception ex)
+ {
+ App.ShowExceptionMessageBox(Properties.Resources.Error_Serializing_Deleted_Layouts, ex);
+ }
+ }
+
+ public void SerializeCreatedCustomZonesets(List models)
+ {
+ CreatedCustomZoneSetsWrapper layouts = new CreatedCustomZoneSetsWrapper
+ {
+ CreatedCustomZoneSets = models,
+ };
+
+ try
+ {
+ string jsonString = JsonSerializer.Serialize(layouts, _options);
+ _fileSystem.File.WriteAllText(AppliedZoneSetTmpFile, jsonString);
+ }
+ catch (Exception ex)
+ {
+ App.ShowExceptionMessageBox(Properties.Resources.Error_Persisting_Custom_Layout, ex);
+ }
+ }
+
+ private LayoutType JsonTagToLayoutType(string tag)
+ {
+ LayoutType type = LayoutType.Blank;
+ switch (tag)
+ {
+ case FocusJsonTag:
+ type = LayoutType.Focus;
+ break;
+ case ColumnsJsonTag:
+ type = LayoutType.Columns;
+ break;
+ case RowsJsonTag:
+ type = LayoutType.Rows;
+ break;
+ case GridJsonTag:
+ type = LayoutType.Grid;
+ break;
+ case PriorityGridJsonTag:
+ type = LayoutType.PriorityGrid;
+ break;
+ case CustomJsonTag:
+ type = LayoutType.Custom;
+ break;
+ }
+
+ return type;
+ }
+
+ private string LayoutTypeToJsonTag(LayoutType type)
+ {
+ switch (type)
+ {
+ case LayoutType.Focus:
+ return FocusJsonTag;
+ case LayoutType.Rows:
+ return RowsJsonTag;
+ case LayoutType.Columns:
+ return ColumnsJsonTag;
+ case LayoutType.Grid:
+ return GridJsonTag;
+ case LayoutType.PriorityGrid:
+ return PriorityGridJsonTag;
+ case LayoutType.Custom:
+ return CustomJsonTag;
+ }
+
+ return string.Empty;
+ }
+
+ private static string ParsingCmdArgsErrorReport(string args, int count, string targetMonitorName, List monitorData, List monitors)
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine();
+ sb.AppendLine("```");
+ sb.AppendLine(" ## Command-line arguments:");
+ sb.AppendLine();
+ sb.AppendLine(args);
+
+ sb.AppendLine();
+ sb.AppendLine("```");
+ sb.AppendLine(" ## Parsed command-line arguments:");
+ sb.AppendLine();
+
+ sb.Append("Span zones across monitors: ");
+ sb.AppendLine(App.Overlay.SpanZonesAcrossMonitors.ToString());
+ sb.Append("Monitors count: ");
+ sb.AppendLine(count.ToString());
+ sb.Append("Target monitor: ");
+ sb.AppendLine(targetMonitorName);
+
+ sb.AppendLine();
+ sb.AppendLine(" # Per monitor data:");
+ sb.AppendLine();
+ foreach (NativeMonitorData data in monitorData)
+ {
+ sb.AppendLine(data.ToString());
+ }
+
+ sb.AppendLine();
+ sb.AppendLine("```");
+ sb.AppendLine(" ## Monitors discovered:");
+ sb.AppendLine();
+
+ foreach (Monitor m in monitors)
+ {
+ sb.AppendLine(m.Device.ToString());
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/MonitorChangedEventArgs.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/MonitorChangedEventArgs.cs
new file mode 100644
index 0000000000..2daa5a3692
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/MonitorChangedEventArgs.cs
@@ -0,0 +1,18 @@
+// 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;
+
+namespace FancyZonesEditor.Utils
+{
+ public class MonitorChangedEventArgs : EventArgs
+ {
+ public int LastMonitor { get; }
+
+ public MonitorChangedEventArgs(int lastMonitor)
+ {
+ LastMonitor = lastMonitor;
+ }
+ }
+}
diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/RelayCommand.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/RelayCommand.cs
new file mode 100644
index 0000000000..8086689b5e
--- /dev/null
+++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/RelayCommand.cs
@@ -0,0 +1,67 @@
+// 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.Windows.Input;
+
+namespace FancyZonesEditor.Utils
+{
+ public class RelayCommand : ICommand
+ {
+ private readonly Predicate