mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 06:29:44 +08:00
[FancyZones] Editor multi monitor support (#6562)
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>
This commit is contained in:
parent
687fc2e169
commit
b8e5ccfb7b
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@ -1456,6 +1456,7 @@ NDEBUG
|
|||||||
ndp
|
ndp
|
||||||
neq
|
neq
|
||||||
NESW
|
NESW
|
||||||
|
netcore
|
||||||
netcoreapp
|
netcoreapp
|
||||||
netframework
|
netframework
|
||||||
netfx
|
netfx
|
||||||
|
@ -13,8 +13,8 @@ Global
|
|||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.ActiveCfg = Debug|x64
|
{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}.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.ActiveCfg = Release|x64
|
||||||
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Debug|x64
|
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -3,4 +3,7 @@
|
|||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
|
||||||
</startup>
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
|
||||||
|
</runtime>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
using System.IO.Abstractions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Utils;
|
||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
@ -24,6 +23,7 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
// Non-localizable strings
|
// Non-localizable strings
|
||||||
private const string CrashReportLogFile = "FZEditorCrashLog.txt";
|
private const string CrashReportLogFile = "FZEditorCrashLog.txt";
|
||||||
|
private const string ErrorReportLogFile = "FZEditorErrorLog.txt";
|
||||||
private const string PowerToysIssuesURL = "https://aka.ms/powerToysReportBug";
|
private const string PowerToysIssuesURL = "https://aka.ms/powerToysReportBug";
|
||||||
|
|
||||||
private const string CrashReportExceptionTag = "Exception";
|
private const string CrashReportExceptionTag = "Exception";
|
||||||
@ -44,57 +44,76 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
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()
|
public App()
|
||||||
{
|
{
|
||||||
ZoneSettings = new Settings();
|
DebugModeCheck();
|
||||||
|
FancyZonesEditorIO = new FancyZonesEditorIO();
|
||||||
|
Overlay = new Overlay();
|
||||||
|
MainWindowSettings = new MainWindowSettingsModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(object sender, StartupEventArgs e)
|
private void OnStartup(object sender, StartupEventArgs e)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||||
|
|
||||||
RunnerHelper.WaitForPowerToysRunner(Settings.PowerToysPID, () =>
|
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
|
||||||
{
|
{
|
||||||
Environment.Exit(0);
|
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)
|
fullMessage += ": " + exception.Message;
|
||||||
{
|
|
||||||
// found match
|
|
||||||
foundModel = model;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundModel == null)
|
MessageBox.Show(fullMessage, FancyZonesEditor.Properties.Resources.Error_Exception_Message_Box_Title);
|
||||||
{
|
}
|
||||||
foreach (LayoutModel model in Settings.CustomModels)
|
|
||||||
{
|
|
||||||
if ("{" + model.Guid.ToString().ToUpper() + "}" == Settings.ActiveZoneSetUUid.ToUpper())
|
|
||||||
{
|
|
||||||
// found match
|
|
||||||
foundModel = model;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundModel == null)
|
public static void ShowExceptionReportMessageBox(string reportData)
|
||||||
{
|
{
|
||||||
foundModel = ZoneSettings.DefaultModels[0];
|
var fileStream = File.OpenWrite(ErrorReportLogFile);
|
||||||
}
|
var sw = new StreamWriter(fileStream);
|
||||||
|
sw.Write(reportData);
|
||||||
|
sw.Flush();
|
||||||
|
fileStream.Close();
|
||||||
|
|
||||||
foundModel.IsSelected = true;
|
ShowReportMessageBox(fileStream.Name);
|
||||||
|
|
||||||
EditorOverlay overlay = new EditorOverlay();
|
|
||||||
overlay.Show();
|
|
||||||
overlay.DataContext = foundModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
|
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
|
||||||
@ -103,16 +122,22 @@ namespace FancyZonesEditor
|
|||||||
var sw = new StreamWriter(fileStream);
|
var sw = new StreamWriter(fileStream);
|
||||||
sw.Write(FormatException((Exception)args.ExceptionObject));
|
sw.Write(FormatException((Exception)args.ExceptionObject));
|
||||||
fileStream.Close();
|
fileStream.Close();
|
||||||
|
|
||||||
|
ShowReportMessageBox(fileStream.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowReportMessageBox(string fileName)
|
||||||
|
{
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part1 +
|
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part1 +
|
||||||
Path.GetFullPath(fileStream.Name) +
|
Path.GetFullPath(fileName) +
|
||||||
"\n" +
|
"\n" +
|
||||||
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part2 +
|
FancyZonesEditor.Properties.Resources.Crash_Report_Message_Box_Text_Part2 +
|
||||||
PowerToysIssuesURL,
|
PowerToysIssuesURL,
|
||||||
FancyZonesEditor.Properties.Resources.Fancy_Zones_Editor_App_Title);
|
FancyZonesEditor.Properties.Resources.Fancy_Zones_Editor_App_Title);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatException(Exception ex)
|
private static string FormatException(Exception ex)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="300" d:DesignWidth="300">
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
<Grid>
|
<Grid x:Name="Body">
|
||||||
<Canvas x:Name="Preview"/>
|
<Viewbox Stretch="Uniform">
|
||||||
|
<Canvas x:Name="Preview"/>
|
||||||
|
</Viewbox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
{
|
{
|
||||||
@ -46,6 +47,10 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void UpdateZoneRects()
|
private void UpdateZoneRects()
|
||||||
{
|
{
|
||||||
|
var workArea = App.Overlay.WorkArea;
|
||||||
|
Preview.Width = workArea.Width;
|
||||||
|
Preview.Height = workArea.Height;
|
||||||
|
|
||||||
UIElementCollection previewChildren = Preview.Children;
|
UIElementCollection previewChildren = Preview.Children;
|
||||||
int previewChildrenCount = previewChildren.Count;
|
int previewChildrenCount = previewChildren.Count;
|
||||||
while (previewChildrenCount < _model.Zones.Count)
|
while (previewChildrenCount < _model.Zones.Count)
|
||||||
@ -54,6 +59,7 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
Model = _model,
|
Model = _model,
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.Children.Add(zone);
|
Preview.Children.Add(zone);
|
||||||
previewChildrenCount++;
|
previewChildrenCount++;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
Background="White"
|
Background="White"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterOwner"
|
||||||
Closed="OnClosed">
|
Closed="OnClosed">
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@ -19,24 +19,28 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
KeyUp += CanvasEditorWindow_KeyUp;
|
KeyUp += CanvasEditorWindow_KeyUp;
|
||||||
|
|
||||||
_model = EditorOverlay.Current.DataContext as CanvasLayoutModel;
|
_model = App.Overlay.CurrentDataContext as CanvasLayoutModel;
|
||||||
_stashedModel = (CanvasLayoutModel)_model.Clone();
|
_stashedModel = (CanvasLayoutModel)_model.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAddZone(object sender, RoutedEventArgs e)
|
private void OnAddZone(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_offset + (int)(Settings.WorkArea.Width * 0.4) < (int)Settings.WorkArea.Width
|
Rect workingArea = App.Overlay.WorkArea;
|
||||||
&& _offset + (int)(Settings.WorkArea.Height * 0.4) < (int)Settings.WorkArea.Height)
|
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
|
else
|
||||||
{
|
{
|
||||||
_offset = 100;
|
_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)
|
protected new void OnCancel(object sender, RoutedEventArgs e)
|
||||||
|
@ -8,6 +8,7 @@ using System.Windows;
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
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 monitorPositionLow = (int)(isX ? singleMonitor.Left : singleMonitor.Top);
|
||||||
int monitorPositionHigh = (int)(isX ? singleMonitor.Right : singleMonitor.Bottom);
|
int monitorPositionHigh = (int)(isX ? singleMonitor.Right : singleMonitor.Bottom);
|
||||||
@ -213,8 +214,9 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private SnappyHelperBase NewDefaultSnappyHelper(bool isX, ResizeMode mode)
|
private SnappyHelperBase NewDefaultSnappyHelper(bool isX, ResizeMode mode)
|
||||||
{
|
{
|
||||||
int screenAxisOrigin = (int)(isX ? Settings.WorkArea.Left : Settings.WorkArea.Top);
|
Rect workingArea = App.Overlay.WorkArea;
|
||||||
int screenAxisSize = (int)(isX ? Settings.WorkArea.Width : Settings.WorkArea.Height);
|
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);
|
return new SnappyHelperMagnetic(Model.Zones, ZoneIndex, isX, mode, screenAxisOrigin, screenAxisSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace FancyZonesEditor.Converters
|
|||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
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)
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for EditorOverlay.xaml
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,8 +13,8 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
protected void OnSaveApplyTemplate(object sender, RoutedEventArgs e)
|
protected void OnSaveApplyTemplate(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
EditorOverlay mainEditor = EditorOverlay.Current;
|
var mainEditor = App.Overlay;
|
||||||
if (mainEditor.DataContext is LayoutModel model)
|
if (mainEditor.CurrentDataContext is LayoutModel model)
|
||||||
{
|
{
|
||||||
// If new custom Canvas layout is created (i.e. edited Blank layout),
|
// If new custom Canvas layout is created (i.e. edited Blank layout),
|
||||||
// it's type needs to be updated
|
// it's type needs to be updated
|
||||||
@ -30,14 +30,14 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
_backToLayoutPicker = false;
|
_backToLayoutPicker = false;
|
||||||
Close();
|
Close();
|
||||||
EditorOverlay.Current.Close();
|
mainEditor.CloseEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void OnClosed(object sender, EventArgs e)
|
protected void OnClosed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_backToLayoutPicker)
|
if (_backToLayoutPicker)
|
||||||
{
|
{
|
||||||
EditorOverlay.Current.ShowLayoutPicker();
|
App.Overlay.CloseEditor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +89,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
|
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
@ -118,9 +121,16 @@
|
|||||||
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
|
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
|
||||||
<Link>GlobalSuppressions.cs</Link>
|
<Link>GlobalSuppressions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Models\LayoutSettings.cs" />
|
||||||
<Compile Include="DashCaseNamingPolicy.cs" />
|
<Compile Include="DashCaseNamingPolicy.cs" />
|
||||||
<Compile Include="GridData.cs" />
|
<Compile Include="GridData.cs" />
|
||||||
<Compile Include="GridDragHandles.cs" />
|
<Compile Include="GridDragHandles.cs" />
|
||||||
|
<Compile Include="LayoutOverlayWindow.xaml.cs">
|
||||||
|
<DependentUpon>LayoutOverlayWindow.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Models\LayoutType.cs" />
|
||||||
|
<Compile Include="Models\Monitor.cs" />
|
||||||
|
<Compile Include="Overlay.cs" />
|
||||||
<Compile Include="StringUtils.cs" />
|
<Compile Include="StringUtils.cs" />
|
||||||
<Compile Include="Converters\BooleanToBrushConverter.xaml.cs" />
|
<Compile Include="Converters\BooleanToBrushConverter.xaml.cs" />
|
||||||
<Compile Include="Converters\BooleanToIntConverter.xaml.cs" />
|
<Compile Include="Converters\BooleanToIntConverter.xaml.cs" />
|
||||||
@ -140,10 +150,7 @@
|
|||||||
<Compile Include="Models\CanvasLayoutModel.cs" />
|
<Compile Include="Models\CanvasLayoutModel.cs" />
|
||||||
<Compile Include="Models\GridLayoutModel.cs" />
|
<Compile Include="Models\GridLayoutModel.cs" />
|
||||||
<Compile Include="Models\LayoutModel.cs" />
|
<Compile Include="Models\LayoutModel.cs" />
|
||||||
<Compile Include="Models\Settings.cs" />
|
<Compile Include="Models\MainWindowSettingsModel.cs" />
|
||||||
<Compile Include="EditorOverlay.xaml.cs">
|
|
||||||
<DependentUpon>EditorOverlay.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Converters\ModelToVisibilityConverter.xaml.cs" />
|
<Compile Include="Converters\ModelToVisibilityConverter.xaml.cs" />
|
||||||
<Compile Include="Native.cs" />
|
<Compile Include="Native.cs" />
|
||||||
<Compile Include="RowColInfo.cs" />
|
<Compile Include="RowColInfo.cs" />
|
||||||
@ -151,6 +158,15 @@
|
|||||||
<DependentUpon>GridEditorWindow.xaml</DependentUpon>
|
<DependentUpon>GridEditorWindow.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="SplitEventArgs.cs" />
|
<Compile Include="SplitEventArgs.cs" />
|
||||||
|
<Compile Include="Utils\FancyZonesEditorIO.cs" />
|
||||||
|
<Compile Include="Utils\EventArgs`1.cs" />
|
||||||
|
<Compile Include="Utils\EventRaiser.cs" />
|
||||||
|
<Compile Include="Utils\MonitorChangedEventArgs.cs" />
|
||||||
|
<Compile Include="Models\MonitorInfoModel.cs" />
|
||||||
|
<Compile Include="Utils\RelayCommand.cs" />
|
||||||
|
<Compile Include="Utils\RelayCommand`1.cs" />
|
||||||
|
<Compile Include="Models\Device.cs" />
|
||||||
|
<Compile Include="ViewModels\MonitorViewModel.cs" />
|
||||||
<Compile Include="WindowLayout.xaml.cs">
|
<Compile Include="WindowLayout.xaml.cs">
|
||||||
<DependentUpon>WindowLayout.xaml</DependentUpon>
|
<DependentUpon>WindowLayout.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -175,6 +191,10 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="LayoutOverlayWindow.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="LayoutPreview.xaml">
|
<Page Include="LayoutPreview.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -191,10 +211,6 @@
|
|||||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Page Include="EditorOverlay.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="GridEditorWindow.xaml">
|
<Page Include="GridEditorWindow.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -235,6 +251,7 @@
|
|||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Properties\Resources.*.resx" />
|
<EmbeddedResource Include="Properties\Resources.*.resx" />
|
||||||
|
<None Include="app.manifest" />
|
||||||
<None Include="Properties\Settings.settings">
|
<None Include="Properties\Settings.settings">
|
||||||
<Generator>SettingsSingleFileGenerator</Generator>
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
@ -253,6 +270,9 @@
|
|||||||
<PackageReference Include="System.IO.Abstractions">
|
<PackageReference Include="System.IO.Abstractions">
|
||||||
<Version>12.2.5</Version>
|
<Version>12.2.5</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.DpiAwareness">
|
||||||
|
<Version>6.6.30107</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="System.Text.Json">
|
<PackageReference Include="System.Text.Json">
|
||||||
<Version>4.7.2</Version>
|
<Version>4.7.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
{
|
{
|
||||||
|
@ -19,33 +19,35 @@
|
|||||||
<Setter Property="Width" Value="150" />
|
<Setter Property="Width" Value="150" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Viewbox Stretch="Uniform">
|
||||||
<Canvas x:Name="Preview" />
|
<Grid>
|
||||||
<Canvas x:Name="AdornerLayer" />
|
<Canvas x:Name="Preview" />
|
||||||
<Canvas
|
<Canvas x:Name="AdornerLayer" />
|
||||||
x:Name="MergePanel"
|
<Canvas
|
||||||
MouseUp="MergePanelMouseUp"
|
x:Name="MergePanel"
|
||||||
Visibility="Collapsed">
|
MouseUp="MergePanelMouseUp"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
x:Name="MergeButtons"
|
x:Name="MergeButtons"
|
||||||
Background="Gray"
|
Background="Gray"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Button
|
<Button
|
||||||
Width="134"
|
Width="134"
|
||||||
Height="36"
|
Height="36"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
Click="MergeClick">
|
Click="MergeClick">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Image
|
<Image
|
||||||
Height="16"
|
Height="16"
|
||||||
Margin="0,0,12,0"
|
Margin="0,0,12,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Source="images/Merge.png" />
|
Source="images/Merge.png" />
|
||||||
<TextBlock Text="Merge zones" />
|
<TextBlock Text="Merge zones" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Viewbox>
|
||||||
</UserControl>
|
</UserControl>
|
@ -8,6 +8,7 @@ using System.Windows;
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
{
|
{
|
||||||
@ -32,7 +33,7 @@ namespace FancyZonesEditor
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Loaded += GridEditor_Loaded;
|
Loaded += GridEditor_Loaded;
|
||||||
Unloaded += GridEditor_Unloaded;
|
Unloaded += GridEditor_Unloaded;
|
||||||
((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||||
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
|
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +70,8 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
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
|
// Only enter if this is the newest instance
|
||||||
if (actualSize.Width > 0 && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
if (actualSize.Width > 0 && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
||||||
@ -248,7 +250,7 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
_dragHandles.UpdateAfterVerticalSplit(foundCol);
|
_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);
|
_dragHandles.AddDragHandle(Orientation.Vertical, foundRow, foundCol, model);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -298,11 +300,12 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
_dragHandles.UpdateAfterHorizontalSplit(foundRow);
|
_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);
|
_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);
|
ArrangeGridRects(actualSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +357,8 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void OnGridDimensionsChanged()
|
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)
|
if (actualSize.Width > 0)
|
||||||
{
|
{
|
||||||
ArrangeGridRects(actualSize);
|
ArrangeGridRects(actualSize);
|
||||||
@ -363,6 +367,10 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void ArrangeGridRects(Size arrangeSize)
|
private void ArrangeGridRects(Size arrangeSize)
|
||||||
{
|
{
|
||||||
|
var workArea = App.Overlay.WorkArea;
|
||||||
|
Preview.Width = workArea.Width;
|
||||||
|
Preview.Height = workArea.Height;
|
||||||
|
|
||||||
GridLayoutModel model = Model;
|
GridLayoutModel model = Model;
|
||||||
if (model == null)
|
if (model == null)
|
||||||
{
|
{
|
||||||
@ -375,7 +383,7 @@ namespace FancyZonesEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
|
||||||
|
|
||||||
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
|
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
|
||||||
|
|
||||||
@ -403,7 +411,7 @@ namespace FancyZonesEditor
|
|||||||
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
|
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
|
||||||
{
|
{
|
||||||
double spacing = 0;
|
double spacing = 0;
|
||||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
|
||||||
if (settings.ShowSpacing)
|
if (settings.ShowSpacing)
|
||||||
{
|
{
|
||||||
spacing = settings.Spacing;
|
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);
|
ArrangeGridRects(actualSize);
|
||||||
AdornerLayer.UpdateLayout();
|
AdornerLayer.UpdateLayout();
|
||||||
}
|
}
|
||||||
@ -433,7 +442,8 @@ namespace FancyZonesEditor
|
|||||||
int index = _data.SwappedIndexAfterResize(resizer);
|
int index = _data.SwappedIndexAfterResize(resizer);
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
Size actualSize = new Size(ActualWidth, ActualHeight);
|
Rect workingArea = App.Overlay.WorkArea;
|
||||||
|
Size actualSize = new Size(workingArea.Width, workingArea.Height);
|
||||||
ArrangeGridRects(actualSize);
|
ArrangeGridRects(actualSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,7 +488,6 @@ namespace FancyZonesEditor
|
|||||||
if (_startDragPos.X != -1)
|
if (_startDragPos.X != -1)
|
||||||
{
|
{
|
||||||
Point dragPos = e.GetPosition(Preview);
|
Point dragPos = e.GetPosition(Preview);
|
||||||
|
|
||||||
_startRow = -1;
|
_startRow = -1;
|
||||||
_endRow = -1;
|
_endRow = -1;
|
||||||
_startCol = -1;
|
_startCol = -1;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
Background="White"
|
Background="White"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterOwner"
|
||||||
Closed="OnClosed">
|
Closed="OnClosed">
|
||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
namespace FancyZonesEditor
|
namespace FancyZonesEditor
|
||||||
{
|
{
|
||||||
@ -19,13 +20,13 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
KeyUp += GridEditorWindow_KeyUp;
|
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)
|
protected new void OnCancel(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnCancel(sender, e);
|
base.OnCancel(sender, e);
|
||||||
GridLayoutModel model = EditorOverlay.Current.DataContext as GridLayoutModel;
|
GridLayoutModel model = App.Overlay.CurrentDataContext as GridLayoutModel;
|
||||||
_stashedModel.RestoreTo(model);
|
_stashedModel.RestoreTo(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,14 +69,14 @@ namespace FancyZonesEditor
|
|||||||
};
|
};
|
||||||
Body.Children.Add(_splitter);
|
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)
|
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == PropertyIsShiftKeyPressedID)
|
if (e.PropertyName == PropertyIsShiftKeyPressedID)
|
||||||
{
|
{
|
||||||
_switchOrientation = ((App)Application.Current).ZoneSettings.IsShiftKeyPressed;
|
_switchOrientation = ((App)Application.Current).MainWindowSettings.IsShiftKeyPressed;
|
||||||
if (_lastPos.X != -1)
|
if (_lastPos.X != -1)
|
||||||
{
|
{
|
||||||
UpdateSplitter();
|
UpdateSplitter();
|
||||||
@ -108,7 +108,7 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
|
||||||
if (!settings.ShowSpacing)
|
if (!settings.ShowSpacing)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -283,7 +283,7 @@ namespace FancyZonesEditor
|
|||||||
private void DoSplit(Orientation orientation, double offset)
|
private void DoSplit(Orientation orientation, double offset)
|
||||||
{
|
{
|
||||||
int spacing = 0;
|
int spacing = 0;
|
||||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
|
||||||
if (settings.ShowSpacing)
|
if (settings.ShowSpacing)
|
||||||
{
|
{
|
||||||
spacing = settings.Spacing;
|
spacing = settings.Spacing;
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
<Window x:Class="FancyZonesEditor.EditorOverlay"
|
<Window x:Class="FancyZonesEditor.LayoutOverlayWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="FancyZones Editor" Height="450" Width="800"
|
Title="FancyZones Layout" Height="450" Width="800"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
WindowStyle="None"
|
WindowStyle="None"
|
||||||
AllowsTransparency="True"
|
AllowsTransparency="True"
|
||||||
Background="Transparent"
|
Background="Transparent"/>
|
||||||
Loaded="OnLoaded"/>
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for LayoutOverlayWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class LayoutOverlayWindow : Window
|
||||||
|
{
|
||||||
|
public LayoutOverlayWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@ -29,11 +29,22 @@ namespace FancyZonesEditor
|
|||||||
private LayoutModel _model;
|
private LayoutModel _model;
|
||||||
private List<Int32Rect> _zones = new List<Int32Rect>();
|
private List<Int32Rect> _zones = new List<Int32Rect>();
|
||||||
|
|
||||||
|
public bool IsActualSize
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsActualSizeProperty); }
|
||||||
|
set { SetValue(IsActualSizeProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public LayoutPreview()
|
public LayoutPreview()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContextChanged += LayoutPreview_DataContextChanged;
|
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)
|
private void LayoutPreview_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||||
@ -42,12 +53,6 @@ namespace FancyZonesEditor
|
|||||||
RenderPreview();
|
RenderPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsActualSize
|
|
||||||
{
|
|
||||||
get { return (bool)GetValue(IsActualSizeProperty); }
|
|
||||||
set { SetValue(IsActualSizeProperty, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == PropertyZoneCountID)
|
if (e.PropertyName == PropertyZoneCountID)
|
||||||
@ -109,27 +114,25 @@ namespace FancyZonesEditor
|
|||||||
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
|
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
|
||||||
select new RowColInfo(percent)).ToArray();
|
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 spacing = settings.ShowSpacing ? settings.Spacing : 0;
|
||||||
|
|
||||||
int width = (int)Settings.WorkArea.Width;
|
var workArea = App.Overlay.WorkArea;
|
||||||
int height = (int)Settings.WorkArea.Height;
|
double width = workArea.Width - (spacing * (cols + 1));
|
||||||
|
double height = workArea.Height - (spacing * (rows + 1));
|
||||||
double totalWidth = width - (spacing * (cols + 1));
|
|
||||||
double totalHeight = height - (spacing * (rows + 1));
|
|
||||||
|
|
||||||
double top = spacing;
|
double top = spacing;
|
||||||
for (int row = 0; row < rows; row++)
|
for (int row = 0; row < rows; row++)
|
||||||
{
|
{
|
||||||
double cellHeight = rowInfo[row].Recalculate(top, totalHeight);
|
double cellHeight = rowInfo[row].Recalculate(top, height);
|
||||||
top += cellHeight + spacing;
|
top += cellHeight + spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
double left = spacing;
|
double left = spacing;
|
||||||
for (int col = 0; col < cols; col++)
|
for (int col = 0; col < cols; col++)
|
||||||
{
|
{
|
||||||
double cellWidth = colInfo[col].Recalculate(left, totalWidth);
|
double cellWidth = colInfo[col].Recalculate(left, width);
|
||||||
left += cellWidth + spacing;
|
left += cellWidth + spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +143,8 @@ namespace FancyZonesEditor
|
|||||||
Body.Children.Add(viewbox);
|
Body.Children.Add(viewbox);
|
||||||
Canvas frame = new Canvas
|
Canvas frame = new Canvas
|
||||||
{
|
{
|
||||||
Width = width,
|
Width = workArea.Width,
|
||||||
Height = height,
|
Height = workArea.Height,
|
||||||
};
|
};
|
||||||
viewbox.Child = frame;
|
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)
|
private void RenderSmallScalePreview(GridLayoutModel grid)
|
||||||
@ -205,7 +216,7 @@ namespace FancyZonesEditor
|
|||||||
Body.ColumnDefinitions.Add(def);
|
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);
|
Thickness margin = new Thickness(settings.ShowSpacing ? settings.Spacing / 20 : 0);
|
||||||
|
|
||||||
List<int> visited = new List<int>();
|
List<int> visited = new List<int>();
|
||||||
@ -265,6 +276,12 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void RenderCanvasPreview(CanvasLayoutModel canvas)
|
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
|
Viewbox viewbox = new Viewbox
|
||||||
{
|
{
|
||||||
Stretch = Stretch.Uniform,
|
Stretch = Stretch.Uniform,
|
||||||
@ -272,10 +289,11 @@ namespace FancyZonesEditor
|
|||||||
Body.Children.Add(viewbox);
|
Body.Children.Add(viewbox);
|
||||||
Canvas frame = new Canvas
|
Canvas frame = new Canvas
|
||||||
{
|
{
|
||||||
Width = Settings.WorkArea.Width,
|
Width = workArea.Width,
|
||||||
Height = Settings.WorkArea.Height,
|
Height = workArea.Height,
|
||||||
};
|
};
|
||||||
viewbox.Child = frame;
|
viewbox.Child = frame;
|
||||||
|
|
||||||
foreach (Int32Rect zone in canvas.Zones)
|
foreach (Int32Rect zone in canvas.Zones)
|
||||||
{
|
{
|
||||||
Rectangle rect = new Rectangle();
|
Rectangle rect = new Rectangle();
|
||||||
@ -288,6 +306,14 @@ namespace FancyZonesEditor
|
|||||||
rect.Fill = Brushes.LightGray;
|
rect.Fill = Brushes.LightGray;
|
||||||
frame.Children.Add(rect);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,324 +1,464 @@
|
|||||||
<Controls:MetroWindow x:Class="FancyZonesEditor.MainWindow"
|
<Controls:MetroWindow x:Class="FancyZonesEditor.MainWindow"
|
||||||
x:Name="MainWindow1"
|
x:Name="MainWindow1"
|
||||||
AutomationProperties.Name="{x:Static props:Resources.Fancy_Zones_Main_Editor}"
|
AutomationProperties.Name="{x:Static props:Resources.Fancy_Zones_Main_Editor}"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
||||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||||
xmlns:Converters="clr-namespace:FancyZonesEditor.Converters"
|
xmlns:Converters="clr-namespace:FancyZonesEditor.Converters"
|
||||||
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
|
xmlns:props="clr-namespace:FancyZonesEditor.Properties" xmlns:local1="clr-namespace:FancyZonesEditor.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title=""
|
Title=""
|
||||||
Width="810"
|
Width="810"
|
||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
Background="White"
|
Background="White"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterOwner"
|
||||||
Initialized="OnInitialized"
|
Initialized="OnInitialized"
|
||||||
Closing="OnClosing">
|
Closing="OnClosing">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<Converters:BooleanToBrushConverter x:Key="BooleanToBrushConverter" />
|
<Converters:BooleanToBrushConverter x:Key="BooleanToBrushConverter" />
|
||||||
<Converters:ModelToVisibilityConverter x:Key="ModelToVisibilityConverter" />
|
<Converters:ModelToVisibilityConverter x:Key="ModelToVisibilityConverter" />
|
||||||
<Converters:BooleanToIntConverter x:Key="BooleanToIntConverter" />
|
<Converters:BooleanToIntConverter x:Key="BooleanToIntConverter" />
|
||||||
|
|
||||||
<Style x:Key="titleText" TargetType="TextBlock">
|
<Style x:Key="titleText" TargetType="TextBlock">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="Bold" />
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
<Setter Property="LineHeight" Value="24" />
|
<Setter Property="LineHeight" Value="24" />
|
||||||
<Setter Property="FontSize" Value="18"/>
|
<Setter Property="FontSize" Value="18"/>
|
||||||
<Setter Property="Margin" Value="16,20,0,0" />
|
<Setter Property="Margin" Value="16,20,0,0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="zoneCount" TargetType="TextBlock">
|
<Style x:Key="zoneCount" TargetType="TextBlock">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="Regular" />
|
<Setter Property="FontWeight" Value="Regular" />
|
||||||
<Setter Property="FontSize" Value="24"/>
|
<Setter Property="FontSize" Value="24"/>
|
||||||
<Setter Property="LineHeight" Value="24" />
|
<Setter Property="LineHeight" Value="24" />
|
||||||
<Setter Property="Margin" Value="20,0,20,0" />
|
<Setter Property="Margin" Value="20,0,20,0" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="tabText" TargetType="TextBlock">
|
<Style x:Key="tabText" TargetType="TextBlock">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="#767676"/>
|
<Setter Property="Foreground" Value="#767676"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="LineHeight" Value="20" />
|
<Setter Property="LineHeight" Value="20" />
|
||||||
<Setter Property="Margin" Value="24,20,0,0" />
|
<Setter Property="Margin" Value="24,20,0,0" />
|
||||||
<Setter Property="TextAlignment" Value="Center" />
|
<Setter Property="TextAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="settingText" TargetType="TextBlock">
|
<Style x:Key="settingText" TargetType="TextBlock">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="Black"/>
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="LineHeight" Value="20" />
|
<Setter Property="LineHeight" Value="20" />
|
||||||
<Setter Property="Margin" Value="7,10,0,0" />
|
<Setter Property="Margin" Value="7,10,0,0" />
|
||||||
<Setter Property="TextAlignment" Value="Left" />
|
<Setter Property="TextAlignment" Value="Left" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="settingCheckBoxText" TargetType="CheckBox">
|
<Style x:Key="settingCheckBoxText" TargetType="CheckBox">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="Black"/>
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="Margin" Value="5,10,0,0" />
|
<Setter Property="Margin" Value="5,10,0,0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="templateTitleText" TargetType="TextBlock">
|
<Style x:Key="templateTitleText" TargetType="TextBlock">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="Black"/>
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="TextAlignment" Value="Center" />
|
<Setter Property="TextAlignment" Value="Center" />
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="customButtonFocusVisualStyle">
|
<Style x:Key="customButtonFocusVisualStyle">
|
||||||
<Setter Property="Control.Template">
|
<Setter Property="Control.Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate>
|
<ControlTemplate>
|
||||||
<Rectangle Margin="1" Stroke="White" StrokeDashArray="1 2" StrokeThickness="1" />
|
<Rectangle Margin="1" Stroke="White" StrokeDashArray="1 2" StrokeThickness="1" />
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="secondaryButton" TargetType="Button">
|
<Style x:Key="secondaryButton" TargetType="Button">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="White"/>
|
<Setter Property="Foreground" Value="White"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="Padding" Value="0,5,0,5"/>
|
<Setter Property="Padding" Value="0,5,0,5"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
<Setter Property="Background" Value="#767676"/>
|
<Setter Property="Background" Value="#767676"/>
|
||||||
<Setter Property="Margin" Value="16,0,0,0" />
|
<Setter Property="Margin" Value="16,0,0,0" />
|
||||||
<Setter Property="Width" Value="378"/>
|
<Setter Property="Width" Value="245"/>
|
||||||
<Setter Property="Height" Value="32"/>
|
<Setter Property="Height" Value="32"/>
|
||||||
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
|
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Button}">
|
<ControlTemplate TargetType="{x:Type Button}">
|
||||||
<Border Background="{TemplateBinding Background}">
|
<Border Background="{TemplateBinding Background}">
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
<Setter Property="Background" Value="#5D5D5D"/>
|
<Setter Property="Background" Value="#5D5D5D"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="primaryButton" TargetType="Button">
|
<Style x:Key="primaryButton" TargetType="Button">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="White"/>
|
<Setter Property="Foreground" Value="White"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="Padding" Value="0,5,0,5"/>
|
<Setter Property="Padding" Value="0,5,0,5"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
<Setter Property="Background" Value="#0078D7"/>
|
<Setter Property="Background" Value="#0078D7"/>
|
||||||
<Setter Property="Margin" Value="16,0,16,0" />
|
<Setter Property="Margin" Value="16,0,0,0" />
|
||||||
<Setter Property="Width" Value="377"/>
|
<Setter Property="Width" Value="245"/>
|
||||||
<Setter Property="Height" Value="32"/>
|
<Setter Property="Height" Value="32"/>
|
||||||
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
|
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type Button}">
|
<ControlTemplate TargetType="{x:Type Button}">
|
||||||
<Border Background="{TemplateBinding Background}">
|
<Border Background="{TemplateBinding Background}">
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
<Setter Property="Background" Value="#024D89"/>
|
<Setter Property="Background" Value="#024D89"/>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="spinnerButton" TargetType="Button">
|
<Style x:Key="spinnerButton" TargetType="Button">
|
||||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
<Setter Property="Foreground" Value="Black"/>
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
<Setter Property="FontSize" Value="24"/>
|
<Setter Property="FontSize" Value="24"/>
|
||||||
<Setter Property="Padding" Value="0,0,0,5"/>
|
<Setter Property="Padding" Value="0,0,0,5"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
<Setter Property="Background" Value="#F2F2F2"/>
|
<Setter Property="Background" Value="#F2F2F2"/>
|
||||||
<Setter Property="Margin" Value="0,0,0,0" />
|
<Setter Property="Margin" Value="0,0,0,0" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="newLayoutButton" TargetType="Button">
|
<Style x:Key="newLayoutButton" TargetType="Button">
|
||||||
<Setter Property="Background" Value="#f2f2f2"/>
|
<Setter Property="Background" Value="#f2f2f2"/>
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
<Setter Property="FontFamily" Value="SegoeUI"/>
|
<Setter Property="FontFamily" Value="SegoeUI"/>
|
||||||
<Setter Property="FontWeight" Value="Light"/>
|
<Setter Property="FontWeight" Value="Light"/>
|
||||||
<Setter Property="FontSize" Value="148"/>
|
<Setter Property="FontSize" Value="148"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style x:Key="textBox" TargetType="TextBox">
|
<Style x:Key="textBox" TargetType="TextBox">
|
||||||
<Setter Property="BorderBrush" Value="#cccccc"/>
|
<Setter Property="BorderBrush" Value="#cccccc"/>
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
<Setter Property="FontFamily" Value="SegoeUI"/>
|
<Setter Property="FontFamily" Value="SegoeUI"/>
|
||||||
<Setter Property="FontWeight" Value="Regular"/>
|
<Setter Property="FontWeight" Value="Regular"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
<Setter Property="Margin" Value="16,6,0,0"/>
|
<Setter Property="Margin" Value="16,6,0,0"/>
|
||||||
<Setter Property="Padding" Value="5,5,5,5"/>
|
<Setter Property="Padding" Value="5,5,5,5"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style x:Key="desktopIdText" TargetType="TextBlock">
|
||||||
<Style x:Key="templateBackground" TargetType="Border">
|
<Setter Property="TextAlignment" Value="Center" />
|
||||||
<Setter Property="Background" Value="#F2F2F2"/>
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<Setter Property="CornerRadius" Value="4"/>
|
<Setter Property="FontWeight" Value="Light" />
|
||||||
<Setter Property="BorderThickness" Value="2"/>
|
<Setter Property="FontSize" Value="42" />
|
||||||
</Style>
|
<Setter Property="Foreground" Value="#303030" />
|
||||||
|
</Style>
|
||||||
<ControlTemplate x:Key="myTabs">
|
<Style x:Key="desktopDimensionsText" TargetType="TextBlock">
|
||||||
<Grid Width="404">
|
<Setter Property="TextAlignment" Value="Center" />
|
||||||
<Grid.RowDefinitions>
|
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||||
<RowDefinition Height="40"/>
|
<Setter Property="FontWeight" Value="Light" />
|
||||||
<RowDefinition Height="2"/>
|
<Setter Property="FontSize" Value="10" />
|
||||||
</Grid.RowDefinitions>
|
<Setter Property="Foreground" Value="#303030" />
|
||||||
<TextBlock Grid.Row="0" Name="myTabText" Text="{TemplateBinding TabItem.Header}" Style="{StaticResource tabText}" />
|
</Style>
|
||||||
<Rectangle Grid.Row="1" x:Name="myTabLine" Fill="#F2F2F2" Height="1"/>
|
<Style x:Key="desktopButtonStyle" TargetType="Button">
|
||||||
</Grid>
|
<Setter Property="Background" Value="#DADADA" />
|
||||||
<ControlTemplate.Triggers>
|
<Setter Property="Width" Value="{Binding DisplayWidth}" />
|
||||||
<Trigger Property="TabItem.IsSelected" Value="True">
|
<Setter Property="Height" Value="{Binding DisplayHeight}" />
|
||||||
<Setter TargetName="myTabText" Property="Foreground" Value="#2A79D7"/>
|
<Setter Property="Template">
|
||||||
<Setter TargetName="myTabLine" Property="Fill" Value="#2A79D7" />
|
<Setter.Value>
|
||||||
<Setter TargetName="myTabLine" Property="Height" Value="2"/>
|
<ControlTemplate TargetType="Button">
|
||||||
</Trigger>
|
<Border Background="{TemplateBinding Background}"
|
||||||
</ControlTemplate.Triggers>
|
CornerRadius="0"
|
||||||
</ControlTemplate>
|
BorderBrush="#303030"
|
||||||
</Window.Resources>
|
BorderThickness="2" >
|
||||||
|
<ContentPresenter x:Name="contentPresenter"
|
||||||
<StackPanel FocusManager.FocusedElement="{Binding ElementName=decrementZones}">
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
<TextBlock Name="DialogTitle" Text="{x:Static props:Resources.Choose_Layout}" Style="{StaticResource titleText}" />
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
<TabControl BorderThickness="0" x:Name="TemplateTab" AutomationProperties.LabeledBy="{Binding ElementName=DialogTitle}" SelectedIndex="{Binding IsCustomLayoutActive, Mode=OneWay, Converter={StaticResource BooleanToIntConverter}}">
|
Margin="{TemplateBinding Padding}" />
|
||||||
<TabItem Header="{x:Static props:Resources.Templates}" Template="{StaticResource myTabs}" AutomationProperties.Name="{x:Static props:Resources.Tab_Item_Templates}">
|
</Border>
|
||||||
<StackPanel>
|
<ControlTemplate.Triggers>
|
||||||
<StackPanel Margin="0,15,0,8" Orientation="Horizontal" HorizontalAlignment="Center">
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
<Button x:Name="decrementZones" Width="40" Height="40" AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}" Content="-" Style="{StaticResource spinnerButton}" Click="DecrementZones_Click"/>
|
<Setter Property="Background" Value="#C2C2C2" />
|
||||||
<TextBlock x:Name="zoneCount" Text="{Binding ZoneCount}" Style="{StaticResource zoneCount}"/>
|
</Trigger>
|
||||||
<Button x:Name="incrementZones" Width="40" Height="40" AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}" Content="+" Style="{StaticResource spinnerButton}" Click="IncrementZones_Click"/>
|
<DataTrigger Binding="{Binding Selected}" Value="true">
|
||||||
</StackPanel>
|
<Setter Property="Background" Value="#0078D7" />
|
||||||
<ItemsControl ItemsSource="{Binding DefaultModels}" Margin="8,0,0,0">
|
</DataTrigger>
|
||||||
<ItemsControl.ItemsPanel>
|
</ControlTemplate.Triggers>
|
||||||
<ItemsPanelTemplate>
|
</ControlTemplate>
|
||||||
<WrapPanel Orientation="Horizontal" ItemWidth="{Binding ElementName=MainWindow1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Path=WrapPanelItemSize}" ItemHeight="{Binding ElementName=WrapPanel1, Path=ItemWidth}" x:Name="WrapPanel1" />
|
</Setter.Value>
|
||||||
</ItemsPanelTemplate>
|
</Setter>
|
||||||
</ItemsControl.ItemsPanel>
|
</Style>
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
<Style x:Key="templateBackground" TargetType="Border">
|
||||||
<Border Margin="8"
|
<Setter Property="Background" Value="#F2F2F2"/>
|
||||||
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
|
<Setter Property="CornerRadius" Value="4"/>
|
||||||
Style="{StaticResource templateBackground}"
|
<Setter Property="BorderThickness" Value="2"/>
|
||||||
MouseDown="LayoutItem_Click"
|
</Style>
|
||||||
Focusable="True"
|
|
||||||
FocusManager.GotFocus="LayoutItem_Focused"
|
<ControlTemplate x:Key="myTabs">
|
||||||
KeyDown="LayoutItem_Apply">
|
<Grid Width="404">
|
||||||
<DockPanel Margin="0,20,0,0"
|
<Grid.RowDefinitions>
|
||||||
VerticalAlignment="Stretch"
|
<RowDefinition Height="40"/>
|
||||||
HorizontalAlignment="Stretch"
|
<RowDefinition Height="2"/>
|
||||||
LastChildFill="True">
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Padding="8,0,8,0"
|
<TextBlock Grid.Row="0" Name="myTabText" Text="{TemplateBinding TabItem.Header}" Style="{StaticResource tabText}" />
|
||||||
TextTrimming="CharacterEllipsis"
|
<Rectangle Grid.Row="1" x:Name="myTabLine" Fill="#F2F2F2" Height="1"/>
|
||||||
DockPanel.Dock="Top"
|
</Grid>
|
||||||
Text="{Binding Name}"
|
<ControlTemplate.Triggers>
|
||||||
Style="{StaticResource templateTitleText}" />
|
<Trigger Property="TabItem.IsSelected" Value="True">
|
||||||
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
<Setter TargetName="myTabText" Property="Foreground" Value="#2A79D7"/>
|
||||||
</DockPanel>
|
<Setter TargetName="myTabLine" Property="Fill" Value="#2A79D7" />
|
||||||
</Border>
|
<Setter TargetName="myTabLine" Property="Height" Value="2"/>
|
||||||
</DataTemplate>
|
</Trigger>
|
||||||
</ItemsControl.ItemTemplate>
|
</ControlTemplate.Triggers>
|
||||||
</ItemsControl>
|
</ControlTemplate>
|
||||||
</StackPanel>
|
|
||||||
</TabItem>
|
<ControlTemplate x:Key="desktopButton">
|
||||||
|
<Button Style="{StaticResource desktopButtonStyle}"
|
||||||
|
Command="{Binding DataContext.SelectCommand, ElementName= MainWindowItemControl}"
|
||||||
<TabItem Header="{x:Static props:Resources.Custom}" Template="{StaticResource myTabs}" AutomationProperties.Name="{x:Static props:Resources.Tab_Item_Custom}">
|
CommandParameter="{Binding}"
|
||||||
<StackPanel>
|
Margin="1,6,1,6">
|
||||||
<ItemsControl ItemsSource="{Binding CustomModels}" Margin="8,8,0,0">
|
<Button.Content>
|
||||||
<ItemsControl.ItemsPanel>
|
<Grid Name="MonitorsGrid">
|
||||||
<ItemsPanelTemplate>
|
<Grid.RowDefinitions>
|
||||||
<WrapPanel Orientation="Horizontal" ItemWidth="{Binding ElementName=MainWindow1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Path=WrapPanelItemSize}" ItemHeight="{Binding ElementName=WrapPanel2, Path=ItemWidth}" x:Name="WrapPanel2" />
|
<RowDefinition Height="*"/>
|
||||||
</ItemsPanelTemplate>
|
<RowDefinition Height="0.40*"/>
|
||||||
</ItemsControl.ItemsPanel>
|
</Grid.RowDefinitions>
|
||||||
<ItemsControl.ItemTemplate>
|
<TextBlock Name="desktopId" Text="{Binding Index}" Style="{StaticResource desktopIdText}" Grid.Row="0"/>
|
||||||
<DataTemplate>
|
<TextBlock Name="desktopDimensions" Text="{Binding Dimensions}" Style="{StaticResource desktopDimensionsText}" Grid.Row="1" />
|
||||||
<Border Margin="8"
|
</Grid>
|
||||||
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
|
</Button.Content>
|
||||||
Style="{StaticResource templateBackground}"
|
</Button>
|
||||||
MouseDown="LayoutItem_Click"
|
|
||||||
Focusable="True"
|
<ControlTemplate.Triggers>
|
||||||
FocusManager.GotFocus="LayoutItem_Focused"
|
<DataTrigger Binding="{Binding Selected}" Value="true">
|
||||||
KeyDown="LayoutItem_Apply">
|
<Setter TargetName="desktopId" Property="Foreground" Value="#F2F2F2" />
|
||||||
<DockPanel Margin="0,20,0,0"
|
<Setter TargetName="desktopDimensions" Property="Foreground" Value="#F2F2F2" />
|
||||||
VerticalAlignment="Stretch"
|
</DataTrigger>
|
||||||
HorizontalAlignment="Stretch"
|
</ControlTemplate.Triggers>
|
||||||
LastChildFill="True">
|
</ControlTemplate>
|
||||||
<DockPanel DockPanel.Dock="Top" LastChildFill="True" >
|
</Window.Resources>
|
||||||
<Button x:Name="DeleteButton" AutomationProperties.Name="{x:Static props:Resources.Custom_Layout_Delete_Button}"
|
|
||||||
Visibility="{Binding Converter={StaticResource ModelToVisibilityConverter}}"
|
<StackPanel FocusManager.FocusedElement="{Binding ElementName=decrementZones}">
|
||||||
DockPanel.Dock="Right"
|
|
||||||
MaxHeight="10"
|
<TextBlock Name="DialogTitle" Text="{x:Static props:Resources.Choose_Layout}" Style="{StaticResource titleText}" />
|
||||||
Click="OnDelete"
|
|
||||||
Padding="8,4,8,4"
|
<Grid>
|
||||||
Margin="0,0,8,0"
|
<Grid.RowDefinitions>
|
||||||
BorderThickness="0"
|
<RowDefinition Height="Auto"/>
|
||||||
Background="#f2f2f2">
|
</Grid.RowDefinitions>
|
||||||
<Image Source="images/Delete.png" />
|
<Grid.ColumnDefinitions>
|
||||||
</Button>
|
<ColumnDefinition Width="*"/>
|
||||||
<TextBlock DockPanel.Dock="Top"
|
</Grid.ColumnDefinitions>
|
||||||
TextTrimming="CharacterEllipsis" Padding="8,0,8,0"
|
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||||
Text="{Binding Name}"
|
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||||
Style="{StaticResource templateTitleText}" />
|
ScrollViewer.CanContentScroll="True"
|
||||||
</DockPanel>
|
ScrollViewer.PanningMode="HorizontalOnly"
|
||||||
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}"
|
||||||
</DockPanel>
|
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
||||||
</Border>
|
<ScrollViewer.DataContext>
|
||||||
</DataTemplate>
|
<local1:MonitorViewModel></local1:MonitorViewModel>
|
||||||
</ItemsControl.ItemTemplate>
|
</ScrollViewer.DataContext>
|
||||||
</ItemsControl>
|
<ItemsControl x:Name="MainWindowItemControl"
|
||||||
|
ItemsSource="{Binding MonitorInfoForViewModel}">
|
||||||
</StackPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
</TabItem>
|
<ItemsPanelTemplate>
|
||||||
</TabControl>
|
<StackPanel Background="Transparent"
|
||||||
|
Orientation="Horizontal"
|
||||||
<Grid Margin="10,4,10,8" >
|
HorizontalAlignment="Center"
|
||||||
<Grid.RowDefinitions>
|
Margin="8, 16, 8, 8"
|
||||||
<RowDefinition Height="1*" />
|
Visibility="{Binding DesktopsPanelVisibility}"/>
|
||||||
<RowDefinition Height="1*" />
|
</ItemsPanelTemplate>
|
||||||
</Grid.RowDefinitions>
|
</ItemsControl.ItemsPanel>
|
||||||
<Grid.ColumnDefinitions>
|
<ItemsControl.ItemTemplate>
|
||||||
<ColumnDefinition Width="1*" />
|
<DataTemplate>
|
||||||
<ColumnDefinition Width="1*" />
|
<Button Template="{StaticResource desktopButton}" />
|
||||||
</Grid.ColumnDefinitions>
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
<CheckBox x:Name="spaceAroundSetting" Content="{x:Static props:Resources.Show_Space_Zones}" Style="{StaticResource settingCheckBoxText}" IsChecked="{Binding ShowSpacing}" Grid.Row="0" Grid.Column="0"/>
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
|
</Grid>
|
||||||
<TextBlock x:Name="paddingValue"
|
|
||||||
Text="{x:Static props:Resources.Space_Around_Zones}"
|
<TabControl BorderThickness="0" x:Name="TemplateTab" AutomationProperties.LabeledBy="{Binding ElementName=DialogTitle}" SelectedIndex="{Binding IsCustomLayoutActive, Mode=OneWay, Converter={StaticResource BooleanToIntConverter}}">
|
||||||
Style="{StaticResource settingText}"
|
<TabItem Header="{x:Static props:Resources.Templates}" Template="{StaticResource myTabs}" AutomationProperties.Name="{x:Static props:Resources.Tab_Item_Templates}">
|
||||||
IsEnabled="{Binding ShowSpacing}"
|
<StackPanel>
|
||||||
MaxWidth="{Binding Path=SettingsTextMaxWidth, ElementName=MainWindow1}"
|
<Grid>
|
||||||
TextWrapping="Wrap"/>
|
<Grid.RowDefinitions>
|
||||||
<TextBox AutomationProperties.LabeledBy="{Binding ElementName=paddingValue}" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}" MinWidth="32" IsEnabled="{Binding ShowSpacing}" />
|
<RowDefinition Height="Auto"/>
|
||||||
</StackPanel>
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Margin="0,15,0,8" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
|
<Button x:Name="decrementZones" Width="40" Height="40" AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}" Content="-" Style="{StaticResource spinnerButton}" Click="DecrementZones_Click"/>
|
||||||
<TextBlock x:Name="sensitivityRadiusValue"
|
<TextBlock x:Name="zoneCount" Text="{Binding ZoneCount}" Style="{StaticResource zoneCount}"/>
|
||||||
Text="{x:Static props:Resources.Distance_adjacent_zones}"
|
<Button x:Name="incrementZones" Width="40" Height="40" AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}" Content="+" Style="{StaticResource spinnerButton}" Click="IncrementZones_Click"/>
|
||||||
Style="{StaticResource settingText}"
|
</StackPanel>
|
||||||
MaxWidth="{Binding Path=SettingsTextMaxWidth, ElementName=MainWindow1}"
|
</Grid>
|
||||||
TextWrapping="Wrap"/>
|
|
||||||
<TextBox AutomationProperties.LabeledBy="{Binding ElementName=sensitivityRadiusValue}" Text="{Binding Path=SensitivityRadius,Mode=TwoWay}" Style="{StaticResource textBox}" MinWidth="40"/>
|
<ItemsControl ItemsSource="{Binding DefaultModels}" Margin="8,0,0,0">
|
||||||
</StackPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
</Grid>
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal"
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,16">
|
Margin="2"
|
||||||
<Button x:Name="EditCustomButton" Content="{x:Static props:Resources.Edit_Selected_Layout}" Padding="8" Style="{StaticResource secondaryButton}" Click="EditLayout_Click"/>
|
ItemWidth="{Binding ElementName=MainWindow1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Path=WrapPanelItemSize}"
|
||||||
<Button x:Name="ApplyCustomButton" Content="{x:Static props:Resources.Apply}" Padding="8" Style="{StaticResource primaryButton}" Click="Apply_Click"/>
|
ItemHeight="{Binding ElementName=WrapPanel1, Path=ItemWidth}"
|
||||||
</StackPanel>
|
x:Name="WrapPanel1" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
</StackPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
</Controls:MetroWindow>
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Margin="8"
|
||||||
|
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
|
||||||
|
Background="{Binding Path=IsApplied, Converter={StaticResource BooleanToBrushConverter}}"
|
||||||
|
Style="{StaticResource templateBackground}"
|
||||||
|
MouseDown="LayoutItem_Click"
|
||||||
|
Focusable="True"
|
||||||
|
FocusManager.GotFocus="LayoutItem_Focused"
|
||||||
|
KeyDown="LayoutItem_Apply">
|
||||||
|
<DockPanel Margin="0,20,0,0"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
LastChildFill="True">
|
||||||
|
<TextBlock Name="layoutName"
|
||||||
|
Padding="8,0,8,0"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
Text="{Binding Name}"
|
||||||
|
Style="{StaticResource templateTitleText}" />
|
||||||
|
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
<DataTemplate.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsApplied}" Value="true">
|
||||||
|
<Setter TargetName="layoutName" Property="Foreground" Value="#F2F2F2" />
|
||||||
|
</DataTrigger>
|
||||||
|
</DataTemplate.Triggers>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
|
||||||
|
<TabItem Header="{x:Static props:Resources.Custom}" Template="{StaticResource myTabs}" AutomationProperties.Name="{x:Static props:Resources.Tab_Item_Custom}">
|
||||||
|
<StackPanel>
|
||||||
|
<ItemsControl ItemsSource="{Binding CustomModels}" Margin="8,8,0,0">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal" ItemWidth="{Binding ElementName=MainWindow1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Path=WrapPanelItemSize}" ItemHeight="{Binding ElementName=WrapPanel2, Path=ItemWidth}" x:Name="WrapPanel2" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Margin="8"
|
||||||
|
BorderBrush="{Binding Path=IsSelected, Converter={StaticResource BooleanToBrushConverter}}"
|
||||||
|
Background="{Binding Path=IsApplied, Converter={StaticResource BooleanToBrushConverter}}"
|
||||||
|
Style="{StaticResource templateBackground}"
|
||||||
|
MouseDown="LayoutItem_Click"
|
||||||
|
Focusable="True"
|
||||||
|
FocusManager.GotFocus="LayoutItem_Focused"
|
||||||
|
KeyDown="LayoutItem_Apply">
|
||||||
|
<DockPanel Margin="0,20,0,0"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
LastChildFill="True">
|
||||||
|
<DockPanel DockPanel.Dock="Top" LastChildFill="True" >
|
||||||
|
<Button x:Name="DeleteButton" AutomationProperties.Name="{x:Static props:Resources.Custom_Layout_Delete_Button}"
|
||||||
|
Visibility="{Binding Converter={StaticResource ModelToVisibilityConverter}}"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
MaxHeight="10"
|
||||||
|
Click="OnDelete"
|
||||||
|
Padding="8,4,8,4"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
BorderThickness="0"
|
||||||
|
Background="#f2f2f2">
|
||||||
|
<Image Source="images/Delete.png" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock Name="layoutName"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
TextTrimming="CharacterEllipsis" Padding="8,0,8,0"
|
||||||
|
Text="{Binding Name}"
|
||||||
|
Style="{StaticResource templateTitleText}" />
|
||||||
|
</DockPanel>
|
||||||
|
<local:LayoutPreview Margin="16" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
<DataTemplate.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsApplied}" Value="true">
|
||||||
|
<Setter TargetName="layoutName" Property="Foreground" Value="#F2F2F2" />
|
||||||
|
</DataTrigger>
|
||||||
|
</DataTemplate.Triggers>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
|
<Grid Margin="10,4,10,8" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="1*" />
|
||||||
|
<RowDefinition Height="1*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="1*" />
|
||||||
|
<ColumnDefinition Width="1*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<CheckBox x:Name="spaceAroundSetting" Content="{x:Static props:Resources.Show_Space_Zones}" Style="{StaticResource settingCheckBoxText}" IsChecked="{Binding ShowSpacing}" Grid.Row="0" Grid.Column="0"/>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
|
||||||
|
<TextBlock x:Name="paddingValue"
|
||||||
|
Text="{x:Static props:Resources.Space_Around_Zones}"
|
||||||
|
Style="{StaticResource settingText}"
|
||||||
|
IsEnabled="{Binding ShowSpacing}"
|
||||||
|
MaxWidth="{Binding Path=SettingsTextMaxWidth, ElementName=MainWindow1}"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
<TextBox AutomationProperties.LabeledBy="{Binding ElementName=paddingValue}" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}" MinWidth="32" IsEnabled="{Binding ShowSpacing}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
|
||||||
|
<TextBlock x:Name="sensitivityRadiusValue"
|
||||||
|
Text="{x:Static props:Resources.Distance_adjacent_zones}"
|
||||||
|
Style="{StaticResource settingText}"
|
||||||
|
MaxWidth="{Binding Path=SettingsTextMaxWidth, ElementName=MainWindow1}"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
<TextBox AutomationProperties.LabeledBy="{Binding ElementName=sensitivityRadiusValue}" Text="{Binding Path=SensitivityRadius,Mode=TwoWay}" Style="{StaticResource textBox}" MinWidth="40"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,10,0,16">
|
||||||
|
<Button x:Name="EditCustomButton" Content="{x:Static props:Resources.Edit_Selected_Layout}" Padding="8" Style="{StaticResource secondaryButton}" Click="EditLayout_Click"/>
|
||||||
|
<Button x:Name="ApplyCustomButton" Content="{x:Static props:Resources.Apply}" Padding="8" Style="{StaticResource primaryButton}" Click="Apply_Click"/>
|
||||||
|
|
||||||
|
<Button x:Name="CloseButton"
|
||||||
|
Content="{x:Static props:Resources.Close}"
|
||||||
|
Visibility="{Binding DesktopsPanelVisibility}"
|
||||||
|
Padding="8"
|
||||||
|
Style="{StaticResource secondaryButton}"
|
||||||
|
Click="CloseButton_Click" >
|
||||||
|
<Button.DataContext>
|
||||||
|
<local1:MonitorViewModel></local1:MonitorViewModel>
|
||||||
|
</Button.DataContext>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Controls:MetroWindow>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@ -19,12 +18,16 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
||||||
public const int MaxZones = 40;
|
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
|
// Localizable string
|
||||||
private static readonly string _defaultNamePrefix = "Custom Layout ";
|
private static readonly string _defaultNamePrefix = "Custom Layout ";
|
||||||
|
|
||||||
public int WrapPanelItemSize { get; set; } = 262;
|
public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize;
|
||||||
|
|
||||||
public double SettingsTextMaxWidth
|
public double SettingsTextMaxWidth
|
||||||
{
|
{
|
||||||
@ -34,20 +37,31 @@ namespace FancyZonesEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow(bool spanZonesAcrossMonitors, Rect workArea)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _settings;
|
DataContext = _settings;
|
||||||
|
|
||||||
KeyUp += MainWindow_KeyUp;
|
KeyUp += MainWindow_KeyUp;
|
||||||
|
|
||||||
if (Settings.WorkArea.Height < 900)
|
if (spanZonesAcrossMonitors)
|
||||||
|
{
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workArea.Height < MinimalForDefaultWrapPanelsHeight || App.Overlay.MultiMonitorMode)
|
||||||
{
|
{
|
||||||
SizeToContent = SizeToContent.WidthAndHeight;
|
SizeToContent = SizeToContent.WidthAndHeight;
|
||||||
WrapPanelItemSize = 180;
|
WrapPanelItemSize = SmallWrapPanelItemSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
DataContext = _settings;
|
||||||
|
SetSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
|
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Escape)
|
if (e.Key == Key.Escape)
|
||||||
@ -101,19 +115,19 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void Select(LayoutModel newSelection)
|
private void Select(LayoutModel newSelection)
|
||||||
{
|
{
|
||||||
if (EditorOverlay.Current.DataContext is LayoutModel currentSelection)
|
if (App.Overlay.CurrentDataContext is LayoutModel currentSelection)
|
||||||
{
|
{
|
||||||
currentSelection.IsSelected = false;
|
currentSelection.IsSelected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
newSelection.IsSelected = true;
|
newSelection.IsSelected = true;
|
||||||
EditorOverlay.Current.DataContext = newSelection;
|
App.Overlay.CurrentDataContext = newSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditLayout_Click(object sender, RoutedEventArgs e)
|
private void EditLayout_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
EditorOverlay mainEditor = EditorOverlay.Current;
|
var mainEditor = App.Overlay;
|
||||||
if (!(mainEditor.DataContext is LayoutModel model))
|
if (!(mainEditor.CurrentDataContext is LayoutModel model))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,19 +135,19 @@ namespace FancyZonesEditor
|
|||||||
model.IsSelected = false;
|
model.IsSelected = false;
|
||||||
Hide();
|
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)
|
if (isPredefinedLayout)
|
||||||
{
|
{
|
||||||
// make a copy
|
// make a copy
|
||||||
model = model.Clone();
|
model = model.Clone();
|
||||||
mainEditor.DataContext = model;
|
mainEditor.CurrentDataContext = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxCustomIndex = 0;
|
int maxCustomIndex = 0;
|
||||||
foreach (LayoutModel customModel in Settings.CustomModels)
|
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
|
||||||
{
|
{
|
||||||
string name = customModel.Name;
|
string name = customModel.Name;
|
||||||
if (name.StartsWith(_defaultNamePrefix))
|
if (name.StartsWith(_defaultNamePrefix))
|
||||||
@ -151,32 +165,7 @@ namespace FancyZonesEditor
|
|||||||
model.Name = _defaultNamePrefix + (++maxCustomIndex);
|
model.Name = _defaultNamePrefix + (++maxCustomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
mainEditor.Edit();
|
mainEditor.OpenEditor(model);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Apply_Click(object sender, RoutedEventArgs e)
|
private void Apply_Click(object sender, RoutedEventArgs e)
|
||||||
@ -186,20 +175,16 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void Apply()
|
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
|
model.Apply();
|
||||||
if (model is CanvasLayoutModel && (model as CanvasLayoutModel).IsScaled)
|
}
|
||||||
{
|
|
||||||
model.Persist();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
model.Apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!mainEditor.MultiMonitorMode)
|
||||||
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,7 +192,7 @@ namespace FancyZonesEditor
|
|||||||
private void OnClosing(object sender, EventArgs e)
|
private void OnClosing(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
LayoutModel.SerializeDeletedCustomZoneSets();
|
LayoutModel.SerializeDeletedCustomZoneSets();
|
||||||
EditorOverlay.Current.Close();
|
App.Overlay.CloseLayoutWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInitialized(object sender, EventArgs e)
|
private void OnInitialized(object sender, EventArgs e)
|
||||||
@ -217,7 +202,7 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
private void SetSelectedItem()
|
private void SetSelectedItem()
|
||||||
{
|
{
|
||||||
foreach (LayoutModel model in Settings.CustomModels)
|
foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
|
||||||
{
|
{
|
||||||
if (model.IsSelected)
|
if (model.IsSelected)
|
||||||
{
|
{
|
||||||
@ -237,5 +222,25 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
model.Delete();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
@ -14,33 +13,21 @@ namespace FancyZonesEditor.Models
|
|||||||
// Free form Layout Model, which specifies independent zone rects
|
// Free form Layout Model, which specifies independent zone rects
|
||||||
public class CanvasLayoutModel : LayoutModel
|
public class CanvasLayoutModel : LayoutModel
|
||||||
{
|
{
|
||||||
// Localizable strings
|
|
||||||
private const string ErrorPersistingCanvasLayout = "Error persisting canvas layout";
|
|
||||||
|
|
||||||
// Non-localizable strings
|
// Non-localizable strings
|
||||||
private const string ModelTypeID = "canvas";
|
private const string ModelTypeID = "canvas";
|
||||||
|
|
||||||
public CanvasLayoutModel(string uuid, string name, LayoutType type, IList<Int32Rect> zones, int workAreaWidth, int workAreaHeight)
|
public Rect CanvasRect { get; private set; }
|
||||||
|
|
||||||
|
public CanvasLayoutModel(string uuid, string name, LayoutType type, IList<Int32Rect> zones, int width, int height)
|
||||||
: base(uuid, name, type)
|
: base(uuid, name, type)
|
||||||
{
|
{
|
||||||
lastWorkAreaWidth = workAreaWidth;
|
Zones = zones;
|
||||||
lastWorkAreaHeight = workAreaHeight;
|
CanvasRect = new Rect(new Size(width, height));
|
||||||
IsScaled = false;
|
|
||||||
|
|
||||||
if (ShouldScaleLayout())
|
|
||||||
{
|
|
||||||
ScaleLayout(zones);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Zones = zones;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanvasLayoutModel(string name, LayoutType type)
|
public CanvasLayoutModel(string name, LayoutType type)
|
||||||
: base(name, type)
|
: base(name, type)
|
||||||
{
|
{
|
||||||
IsScaled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanvasLayoutModel(string name)
|
public CanvasLayoutModel(string name)
|
||||||
@ -51,12 +38,6 @@ namespace FancyZonesEditor.Models
|
|||||||
// Zones - the list of all zones in this layout, described as independent rectangles
|
// Zones - the list of all zones in this layout, described as independent rectangles
|
||||||
public IList<Int32Rect> Zones { get; private set; } = new List<Int32Rect>();
|
public IList<Int32Rect> Zones { get; private set; } = new List<Int32Rect>();
|
||||||
|
|
||||||
private int lastWorkAreaWidth = (int)Settings.WorkArea.Width;
|
|
||||||
|
|
||||||
private int lastWorkAreaHeight = (int)Settings.WorkArea.Height;
|
|
||||||
|
|
||||||
public bool IsScaled { get; private set; }
|
|
||||||
|
|
||||||
// RemoveZoneAt
|
// RemoveZoneAt
|
||||||
// Removes the specified index from the Zones list, and fires a property changed notification for the Zones property
|
// Removes the specified index from the Zones list, and fires a property changed notification for the Zones property
|
||||||
public void RemoveZoneAt(int index)
|
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<Int32Rect> 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
|
private struct Zone
|
||||||
{
|
{
|
||||||
public int X { get; set; }
|
public int X { get; set; }
|
||||||
@ -165,13 +118,15 @@ namespace FancyZonesEditor.Models
|
|||||||
// Implements the LayoutModel.PersistData abstract method
|
// Implements the LayoutModel.PersistData abstract method
|
||||||
protected override void PersistData()
|
protected override void PersistData()
|
||||||
{
|
{
|
||||||
|
AddCustomLayout(this);
|
||||||
|
|
||||||
CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo
|
CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo
|
||||||
{
|
{
|
||||||
RefWidth = lastWorkAreaWidth,
|
RefWidth = (int)CanvasRect.Width,
|
||||||
RefHeight = lastWorkAreaHeight,
|
RefHeight = (int)CanvasRect.Height,
|
||||||
|
|
||||||
Zones = new Zone[Zones.Count],
|
Zones = new Zone[Zones.Count],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < Zones.Count; ++i)
|
for (int i = 0; i < Zones.Count; ++i)
|
||||||
{
|
{
|
||||||
Zone zone = new Zone
|
Zone zone = new Zone
|
||||||
@ -187,26 +142,19 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
CanvasLayoutJson jsonObj = new CanvasLayoutJson
|
CanvasLayoutJson jsonObj = new CanvasLayoutJson
|
||||||
{
|
{
|
||||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
Uuid = Uuid,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
Type = ModelTypeID,
|
Type = ModelTypeID,
|
||||||
Info = layoutInfo,
|
Info = layoutInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
JsonSerializerOptions options = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
||||||
{
|
AddCustomLayoutJson(JsonSerializer.Deserialize<JsonElement>(jsonString));
|
||||||
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
SerializeCreatedCustomZonesets();
|
||||||
FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ShowExceptionMessageBox(ErrorPersistingCanvasLayout, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,9 +13,6 @@ namespace FancyZonesEditor.Models
|
|||||||
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
|
// Grid-styled Layout Model, which specifies rows, columns, percentage sizes, and row/column spans
|
||||||
public class GridLayoutModel : LayoutModel
|
public class GridLayoutModel : LayoutModel
|
||||||
{
|
{
|
||||||
// Localizable strings
|
|
||||||
private const string ErrorPersistingGridLayout = "Error persisting grid layout";
|
|
||||||
|
|
||||||
// Non-localizable strings
|
// Non-localizable strings
|
||||||
private const string ModelTypeID = "grid";
|
private const string ModelTypeID = "grid";
|
||||||
|
|
||||||
@ -204,6 +201,8 @@ namespace FancyZonesEditor.Models
|
|||||||
// Implements the LayoutModel.PersistData abstract method
|
// Implements the LayoutModel.PersistData abstract method
|
||||||
protected override void PersistData()
|
protected override void PersistData()
|
||||||
{
|
{
|
||||||
|
AddCustomLayout(this);
|
||||||
|
|
||||||
GridLayoutInfo layoutInfo = new GridLayoutInfo
|
GridLayoutInfo layoutInfo = new GridLayoutInfo
|
||||||
{
|
{
|
||||||
Rows = Rows,
|
Rows = Rows,
|
||||||
@ -212,6 +211,7 @@ namespace FancyZonesEditor.Models
|
|||||||
ColumnsPercentage = ColumnPercents,
|
ColumnsPercentage = ColumnPercents,
|
||||||
CellChildMap = new int[Rows][],
|
CellChildMap = new int[Rows][],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int row = 0; row < Rows; row++)
|
for (int row = 0; row < Rows; row++)
|
||||||
{
|
{
|
||||||
layoutInfo.CellChildMap[row] = new int[Columns];
|
layoutInfo.CellChildMap[row] = new int[Columns];
|
||||||
@ -223,7 +223,7 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
GridLayoutJson jsonObj = new GridLayoutJson
|
GridLayoutJson jsonObj = new GridLayoutJson
|
||||||
{
|
{
|
||||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
Uuid = Uuid,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
Type = ModelTypeID,
|
Type = ModelTypeID,
|
||||||
Info = layoutInfo,
|
Info = layoutInfo,
|
||||||
@ -233,15 +233,9 @@ namespace FancyZonesEditor.Models
|
|||||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
||||||
{
|
AddCustomLayoutJson(JsonSerializer.Deserialize<JsonElement>(jsonString));
|
||||||
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
SerializeCreatedCustomZonesets();
|
||||||
FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ShowExceptionMessageBox(ErrorPersistingGridLayout, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,77 +6,15 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Windows;
|
|
||||||
|
|
||||||
namespace FancyZonesEditor.Models
|
namespace FancyZonesEditor.Models
|
||||||
{
|
{
|
||||||
public enum LayoutType
|
|
||||||
{
|
|
||||||
Blank = -1,
|
|
||||||
Focus,
|
|
||||||
Columns,
|
|
||||||
Rows,
|
|
||||||
Grid,
|
|
||||||
PriorityGrid,
|
|
||||||
Custom,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base LayoutModel
|
// Base LayoutModel
|
||||||
// Manages common properties and base persistence
|
// Manages common properties and base persistence
|
||||||
public abstract class LayoutModel : INotifyPropertyChanged
|
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()
|
protected LayoutModel()
|
||||||
{
|
{
|
||||||
_guid = Guid.NewGuid();
|
_guid = Guid.NewGuid();
|
||||||
@ -136,6 +74,14 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
private Guid _guid;
|
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
|
// 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
|
// TODO: once we switch to a picker per monitor, we need to move this state to the view
|
||||||
public bool IsSelected
|
public bool IsSelected
|
||||||
@ -157,6 +103,25 @@ namespace FancyZonesEditor.Models
|
|||||||
|
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public bool IsApplied
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _isApplied;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isApplied != value)
|
||||||
|
{
|
||||||
|
_isApplied = value;
|
||||||
|
FirePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isApplied;
|
||||||
|
|
||||||
// implementation of INotifyPropertyChanged
|
// implementation of INotifyPropertyChanged
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
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<string> 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()
|
public static void SerializeDeletedCustomZoneSets()
|
||||||
{
|
{
|
||||||
DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper
|
App.FancyZonesEditorIO.SerializeDeletedCustomZoneSets(_deletedCustomModels);
|
||||||
{
|
}
|
||||||
DeletedCustomZoneSets = _deletedCustomModels,
|
|
||||||
};
|
|
||||||
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
public static void SerializeCreatedCustomZonesets()
|
||||||
{
|
{
|
||||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
App.FancyZonesEditorIO.SerializeCreatedCustomZonesets(_createdCustomLayouts);
|
||||||
};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string jsonString = JsonSerializer.Serialize(deletedLayouts, options);
|
|
||||||
FileSystem.File.WriteAllText(Settings.DeletedCustomZoneSetsTmpFile, jsonString);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ShowExceptionMessageBox(ErrorSerializingDeletedLayouts, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads all the custom Layouts from tmp file passed by FancyZonesLib
|
// Loads all the custom Layouts from tmp file passed by FancyZonesLib
|
||||||
public static ObservableCollection<LayoutModel> LoadCustomModels()
|
public static ObservableCollection<LayoutModel> LoadCustomModels()
|
||||||
{
|
{
|
||||||
_customModels = new ObservableCollection<LayoutModel>();
|
_customModels = new ObservableCollection<LayoutModel>();
|
||||||
|
App.FancyZonesEditorIO.ParseLayouts(ref _customModels, ref _deletedCustomModels);
|
||||||
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<int> rowsPercentage = new List<int>(rows);
|
|
||||||
JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty(RowsPercentageJsonTag).EnumerateArray();
|
|
||||||
|
|
||||||
List<int> columnsPercentage = new List<int>(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<Int32Rect> zones = new List<Int32Rect>();
|
|
||||||
|
|
||||||
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<LayoutModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _customModels;
|
return _customModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObservableCollection<LayoutModel> _customModels = null;
|
private static ObservableCollection<LayoutModel> _customModels = null;
|
||||||
private static List<string> _deletedCustomModels = new List<string>();
|
private static List<string> _deletedCustomModels = new List<string>();
|
||||||
|
private static List<JsonElement> _createdCustomLayouts = new List<JsonElement>();
|
||||||
|
|
||||||
// Callbacks that the base LayoutModel makes to derived types
|
// Callbacks that the base LayoutModel makes to derived types
|
||||||
protected abstract void PersistData();
|
protected abstract void PersistData();
|
||||||
@ -369,83 +200,18 @@ namespace FancyZonesEditor.Models
|
|||||||
Apply();
|
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()
|
public void Apply()
|
||||||
{
|
{
|
||||||
ActiveZoneSetWrapper activeZoneSet = new ActiveZoneSetWrapper
|
MainWindowSettingsModel settings = ((App)App.Current).MainWindowSettings;
|
||||||
{
|
settings.ResetAppliedModel();
|
||||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
IsApplied = true;
|
||||||
};
|
|
||||||
|
|
||||||
switch (Type)
|
// update settings
|
||||||
{
|
App.Overlay.CurrentLayoutSettings.ZonesetUuid = Uuid;
|
||||||
case LayoutType.Focus:
|
App.Overlay.CurrentLayoutSettings.Type = Type;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
// update temp file
|
||||||
|
App.FancyZonesEditorIO.SerializeAppliedLayouts();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
@ -6,10 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
|
|
||||||
@ -18,30 +15,16 @@ namespace FancyZonesEditor
|
|||||||
// Settings
|
// Settings
|
||||||
// These are the configuration settings used by the rest of the editor
|
// 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
|
// 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,
|
Name = 0,
|
||||||
PowerToysPID,
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum WorkAreaCmdArgElements
|
|
||||||
{
|
|
||||||
X = 0,
|
|
||||||
Y,
|
|
||||||
Width,
|
Width,
|
||||||
Height,
|
Height,
|
||||||
|
VirtualDesktopId,
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ParseDeviceMode
|
|
||||||
{
|
|
||||||
Prod,
|
|
||||||
Debug,
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly IFileSystem _fileSystem = new FileSystem();
|
|
||||||
|
|
||||||
private static CanvasLayoutModel _blankCustomModel;
|
private static CanvasLayoutModel _blankCustomModel;
|
||||||
private readonly CanvasLayoutModel _focusModel;
|
private readonly CanvasLayoutModel _focusModel;
|
||||||
private readonly GridLayoutModel _rowsModel;
|
private readonly GridLayoutModel _rowsModel;
|
||||||
@ -63,33 +46,9 @@ namespace FancyZonesEditor
|
|||||||
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
|
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
|
||||||
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
|
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 LayoutTypeBlankStr = "blank";
|
||||||
private const string NullUuidStr = "null";
|
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"
|
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
|
||||||
private static readonly byte[][] _priorityData = new byte[][]
|
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
|
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
|
||||||
DefaultModels = new List<LayoutModel>(5);
|
DefaultModels = new List<LayoutModel>(5);
|
||||||
_focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
|
_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);
|
_blankCustomModel = new CanvasLayoutModel(Properties.Resources.Custom_Layout_Create_New, LayoutType.Blank);
|
||||||
|
|
||||||
UpdateLayoutModels();
|
UpdateTemplateLayoutModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZoneCount - number of zones selected in the picker window
|
// ZoneCount - number of zones selected in the picker window
|
||||||
@ -172,82 +120,79 @@ namespace FancyZonesEditor
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _zoneCount;
|
return App.Overlay.CurrentLayoutSettings.ZoneCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_zoneCount != value)
|
if (App.Overlay.CurrentLayoutSettings.ZoneCount != value)
|
||||||
{
|
{
|
||||||
_zoneCount = value;
|
App.Overlay.CurrentLayoutSettings.ZoneCount = value;
|
||||||
UpdateLayoutModels();
|
UpdateTemplateLayoutModels();
|
||||||
FirePropertyChanged();
|
FirePropertyChanged(nameof(ZoneCount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _zoneCount;
|
|
||||||
|
|
||||||
// Spacing - how much space in between zones of the grid do you want
|
// Spacing - how much space in between zones of the grid do you want
|
||||||
public int Spacing
|
public int Spacing
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _spacing;
|
return App.Overlay.CurrentLayoutSettings.Spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_spacing != value)
|
value = Math.Max(0, value);
|
||||||
|
if (App.Overlay.CurrentLayoutSettings.Spacing != value)
|
||||||
{
|
{
|
||||||
_spacing = Math.Max(MaxNegativeSpacing, value);
|
App.Overlay.CurrentLayoutSettings.Spacing = value;
|
||||||
FirePropertyChanged();
|
UpdateTemplateLayoutModels();
|
||||||
|
FirePropertyChanged(nameof(Spacing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _spacing;
|
|
||||||
|
|
||||||
// ShowSpacing - is the Spacing value used or ignored?
|
// ShowSpacing - is the Spacing value used or ignored?
|
||||||
public bool ShowSpacing
|
public bool ShowSpacing
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _showSpacing;
|
return App.Overlay.CurrentLayoutSettings.ShowSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_showSpacing != value)
|
if (App.Overlay.CurrentLayoutSettings.ShowSpacing != value)
|
||||||
{
|
{
|
||||||
_showSpacing = value;
|
App.Overlay.CurrentLayoutSettings.ShowSpacing = value;
|
||||||
FirePropertyChanged();
|
UpdateTemplateLayoutModels();
|
||||||
|
FirePropertyChanged(nameof(ShowSpacing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _showSpacing;
|
|
||||||
|
|
||||||
// SensitivityRadius - how much space inside the zone to highlight the adjacent zone too
|
// SensitivityRadius - how much space inside the zone to highlight the adjacent zone too
|
||||||
public int SensitivityRadius
|
public int SensitivityRadius
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _sensitivityRadius;
|
return App.Overlay.CurrentLayoutSettings.SensitivityRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_sensitivityRadius != value)
|
value = Math.Max(0, value);
|
||||||
|
if (App.Overlay.CurrentLayoutSettings.SensitivityRadius != value)
|
||||||
{
|
{
|
||||||
_sensitivityRadius = Math.Max(0, value);
|
App.Overlay.CurrentLayoutSettings.SensitivityRadius = value;
|
||||||
FirePropertyChanged();
|
UpdateTemplateLayoutModels();
|
||||||
|
FirePropertyChanged(nameof(SensitivityRadius));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _sensitivityRadius;
|
|
||||||
|
|
||||||
// IsShiftKeyPressed - is the shift key currently being held down
|
// IsShiftKeyPressed - is the shift key currently being held down
|
||||||
public bool IsShiftKeyPressed
|
public bool IsShiftKeyPressed
|
||||||
{
|
{
|
||||||
@ -261,7 +206,7 @@ namespace FancyZonesEditor
|
|||||||
if (_isShiftKeyPressed != value)
|
if (_isShiftKeyPressed != value)
|
||||||
{
|
{
|
||||||
_isShiftKeyPressed = value;
|
_isShiftKeyPressed = value;
|
||||||
FirePropertyChanged();
|
FirePropertyChanged(nameof(IsShiftKeyPressed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,41 +226,16 @@ namespace FancyZonesEditor
|
|||||||
if (_isCtrlKeyPressed != value)
|
if (_isCtrlKeyPressed != value)
|
||||||
{
|
{
|
||||||
_isCtrlKeyPressed = value;
|
_isCtrlKeyPressed = value;
|
||||||
FirePropertyChanged();
|
FirePropertyChanged(nameof(IsCtrlKeyPressed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _isCtrlKeyPressed;
|
private bool _isCtrlKeyPressed;
|
||||||
|
|
||||||
public static Rect WorkArea { get; private set; }
|
|
||||||
|
|
||||||
public static List<Rect> 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
|
// UpdateLayoutModels
|
||||||
// Update the five default layouts based on the new ZoneCount
|
// Update the five default layouts based on the new ZoneCount
|
||||||
private void UpdateLayoutModels()
|
private void UpdateTemplateLayoutModels()
|
||||||
{
|
{
|
||||||
// Update the "Focus" Default Layout
|
// Update the "Focus" Default Layout
|
||||||
_focusModel.Zones.Clear();
|
_focusModel.Zones.Clear();
|
||||||
@ -328,9 +248,13 @@ namespace FancyZonesEditor
|
|||||||
|
|
||||||
// If changing focus layout zones size and/or increment,
|
// If changing focus layout zones size and/or increment,
|
||||||
// same change should be applied in ZoneSet.cpp (ZoneSet::CalculateFocusLayout)
|
// 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));
|
var workingArea = App.Overlay.WorkArea;
|
||||||
int focusRectXIncrement = (ZoneCount <= 1) ? 0 : 50;
|
int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers
|
||||||
int focusRectYIncrement = (ZoneCount <= 1) ? 0 : 50;
|
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++)
|
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<Rect> { 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<LayoutModel> DefaultModels { get; }
|
public IList<LayoutModel> DefaultModels { get; }
|
||||||
|
|
||||||
public static ObservableCollection<LayoutModel> CustomModels
|
public static ObservableCollection<LayoutModel> CustomModels
|
||||||
@ -567,6 +369,120 @@ namespace FancyZonesEditor
|
|||||||
return model.Type != LayoutType.Custom;
|
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
|
// implementation of INotifyPropertyChanged
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
397
src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs
Normal file
397
src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs
Normal file
@ -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<Monitor> 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<Rect> 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<Rect>();
|
||||||
|
Monitors = new List<Monitor>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,15 @@ namespace FancyZonesEditor.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Close.
|
||||||
|
/// </summary>
|
||||||
|
public static string Close {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Close", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Error logged to .
|
/// Looks up a localized string similar to Error logged to .
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -186,6 +195,24 @@ namespace FancyZonesEditor.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Error applying layout.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Applying_Layout {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Applying_Layout", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to FancyZones Editor Exception Handler.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Exception_Message_Box_Title {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Exception_Message_Box_Title", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to FancyZones Editor arguments are invalid..
|
/// Looks up a localized string similar to FancyZones Editor arguments are invalid..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -195,6 +222,24 @@ namespace FancyZonesEditor.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Layout '{0}' has malformed data.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Layout_Malformed_Data {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Layout_Malformed_Data", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Error loading custom layouts.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Loading_Custom_Layouts {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Loading_Custom_Layouts", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to FancyZones Editor Error.
|
/// Looks up a localized string similar to FancyZones Editor Error.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -204,6 +249,15 @@ namespace FancyZonesEditor.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Match not found ({0}).
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Monitor_Match_Not_Found {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Monitor_Match_Not_Found", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to FancyZones Editor should not be run as standalone application..
|
/// Looks up a localized string similar to FancyZones Editor should not be run as standalone application..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -222,6 +276,33 @@ namespace FancyZonesEditor.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Error persisting custom layout.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Persisting_Custom_Layout {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Persisting_Custom_Layout", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Please report the bug to .
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Report {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Report", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Error serializing deleted layouts.
|
||||||
|
/// </summary>
|
||||||
|
public static string Error_Serializing_Deleted_Layouts {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_Serializing_Deleted_Layouts", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to FancyZones Editor.
|
/// Looks up a localized string similar to FancyZones Editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -226,4 +226,31 @@
|
|||||||
<data name="Tab_Item_Templates" xml:space="preserve">
|
<data name="Tab_Item_Templates" xml:space="preserve">
|
||||||
<value>Templates tab selected, press ctrl + tab to switch to Custom</value>
|
<value>Templates tab selected, press ctrl + tab to switch to Custom</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Close" xml:space="preserve">
|
||||||
|
<value>Close</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Applying_Layout" xml:space="preserve">
|
||||||
|
<value>Error applying layout</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Layout_Malformed_Data" xml:space="preserve">
|
||||||
|
<value>Layout '{0}' has malformed data</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Loading_Custom_Layouts" xml:space="preserve">
|
||||||
|
<value>Error loading custom layouts</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Persisting_Custom_Layout" xml:space="preserve">
|
||||||
|
<value>Error persisting custom layout</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Serializing_Deleted_Layouts" xml:space="preserve">
|
||||||
|
<value>Error serializing deleted layouts</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Exception_Message_Box_Title" xml:space="preserve">
|
||||||
|
<value>FancyZones Editor Exception Handler</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Report" xml:space="preserve">
|
||||||
|
<value>Please report the bug to </value>
|
||||||
|
</data>
|
||||||
|
<data name="Error_Monitor_Match_Not_Found" xml:space="preserve">
|
||||||
|
<value>Match not found ({0})</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -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<T> : EventArgs
|
||||||
|
{
|
||||||
|
public EventArgs(T value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Value { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -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<T>(this EventHandler<EventArgs<T>> handler, object sender, T value)
|
||||||
|
{
|
||||||
|
handler?.Invoke(sender, new EventArgs<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Raise<T>(this EventHandler<T> handler, object sender, T value)
|
||||||
|
where T : EventArgs
|
||||||
|
{
|
||||||
|
handler?.Invoke(sender, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value)
|
||||||
|
{
|
||||||
|
handler?.Invoke(sender, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<AppliedZoneSet> AppliedZonesets { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DeletedCustomZoneSetsWrapper
|
||||||
|
{
|
||||||
|
public List<string> DeletedCustomZoneSets { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CreatedCustomZoneSetsWrapper
|
||||||
|
{
|
||||||
|
public List<JsonElement> 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> nativeMonitorData = new List<NativeMonitorData>();
|
||||||
|
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<LayoutModel> custom, ref List<string> 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<int> rowsPercentage = new List<int>(rows);
|
||||||
|
JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty(RowsPercentageJsonTag).EnumerateArray();
|
||||||
|
|
||||||
|
List<int> columnsPercentage = new List<int>(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<Int32Rect> zones = new List<Int32Rect>();
|
||||||
|
|
||||||
|
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<AppliedZoneSet>();
|
||||||
|
|
||||||
|
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<string> 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<JsonElement> 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<NativeMonitorData> monitorData, List<Monitor> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<object> _canExecute;
|
||||||
|
private readonly Action<object> _execute;
|
||||||
|
|
||||||
|
public RelayCommand(Action<object> execute)
|
||||||
|
: this(execute, null)
|
||||||
|
{
|
||||||
|
_execute = execute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
|
||||||
|
{
|
||||||
|
if (execute == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("execute");
|
||||||
|
}
|
||||||
|
|
||||||
|
_execute = execute;
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return _canExecute == null || _canExecute(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
_execute(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures WPF commanding infrastructure asks all RelayCommand objects whether their
|
||||||
|
// associated views should be enabled whenever a command is invoked
|
||||||
|
public event EventHandler CanExecuteChanged
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
CommandManager.RequerySuggested += value;
|
||||||
|
CanExecuteChangedInternal += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
CommandManager.RequerySuggested -= value;
|
||||||
|
CanExecuteChangedInternal -= value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private event EventHandler CanExecuteChangedInternal;
|
||||||
|
|
||||||
|
public void RaiseCanExecuteChanged()
|
||||||
|
{
|
||||||
|
CanExecuteChangedInternal.Invoke(this, null);
|
||||||
|
/*CanExecuteChangedInternal.Raise(this);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// 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<T> : ICommand
|
||||||
|
{
|
||||||
|
private readonly Predicate<T> _canExecute;
|
||||||
|
private readonly Action<T> _execute;
|
||||||
|
|
||||||
|
public RelayCommand(Action<T> execute)
|
||||||
|
: this(execute, null)
|
||||||
|
{
|
||||||
|
_execute = execute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
|
||||||
|
{
|
||||||
|
if (execute == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("execute");
|
||||||
|
}
|
||||||
|
|
||||||
|
_execute = execute;
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return _canExecute == null || _canExecute((T)parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
_execute((T)parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler CanExecuteChanged
|
||||||
|
{
|
||||||
|
add { CommandManager.RequerySuggested += value; }
|
||||||
|
remove { CommandManager.RequerySuggested -= value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows;
|
||||||
|
using FancyZonesEditor.Utils;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor.ViewModels
|
||||||
|
{
|
||||||
|
public class MonitorViewModel : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private const int MaxPreviewDisplaySize = 160;
|
||||||
|
private const int MinPreviewDisplaySize = 98;
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public delegate void MonitorChangedEventHandler(MonitorChangedEventArgs args);
|
||||||
|
|
||||||
|
public ObservableCollection<MonitorInfoModel> MonitorInfoForViewModel { get; set; }
|
||||||
|
|
||||||
|
public static double DesktopPreviewMultiplier { get; private set; }
|
||||||
|
|
||||||
|
public Visibility DesktopsPanelVisibility
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return App.Overlay.MultiMonitorMode ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RelayCommand AddCommand { get; set; }
|
||||||
|
|
||||||
|
public RelayCommand DeleteCommand { get; set; }
|
||||||
|
|
||||||
|
public RelayCommand<MonitorInfoModel> SelectCommand { get; set; }
|
||||||
|
|
||||||
|
public MonitorViewModel()
|
||||||
|
{
|
||||||
|
SelectCommand = new RelayCommand<MonitorInfoModel>(SelectCommandExecute, SelectCommandCanExecute);
|
||||||
|
|
||||||
|
MonitorInfoForViewModel = new ObservableCollection<MonitorInfoModel>();
|
||||||
|
double maxDimension = 0, minDimension = double.MaxValue;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
foreach (var monitor in App.Overlay.Monitors)
|
||||||
|
{
|
||||||
|
Device device = monitor.Device;
|
||||||
|
var bounds = device.ScaledBounds;
|
||||||
|
maxDimension = System.Math.Max(System.Math.Max(maxDimension, bounds.Height), bounds.Width);
|
||||||
|
minDimension = System.Math.Min(System.Math.Min(minDimension, bounds.Height), bounds.Width);
|
||||||
|
|
||||||
|
MonitorInfoForViewModel.Add(new MonitorInfoModel(i, (int)bounds.Height, (int)bounds.Width, device.Dpi, App.Overlay.CurrentDesktop == i - 1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
double maxMultiplier = MaxPreviewDisplaySize / maxDimension;
|
||||||
|
double minMultiplier = MinPreviewDisplaySize / minDimension;
|
||||||
|
DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaisePropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SelectCommandCanExecute(MonitorInfoModel monitorInfo)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectCommandExecute(MonitorInfoModel monitorInfo)
|
||||||
|
{
|
||||||
|
MonitorInfoForViewModel[App.Overlay.CurrentDesktop].Selected = false;
|
||||||
|
MonitorInfoForViewModel[monitorInfo.Index - 1].Selected = true;
|
||||||
|
|
||||||
|
App.Overlay.CurrentDesktop = monitorInfo.Index - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/modules/fancyzones/editor/FancyZonesEditor/app.manifest
Normal file
74
src/modules/fancyzones/editor/FancyZonesEditor/app.manifest
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC Manifest Options
|
||||||
|
If you want to change the Windows User Account Control level replace the
|
||||||
|
requestedExecutionLevel node with one of the following.
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||||
|
Remove this element if your application requires this virtualization for backwards
|
||||||
|
compatibility.
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||||
|
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||||
|
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||||
|
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">System</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
@ -592,133 +592,99 @@ void FancyZones::ToggleEditor() noexcept
|
|||||||
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
|
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
HMONITOR monitor{};
|
|
||||||
HWND foregroundWindow{};
|
|
||||||
|
|
||||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
|
||||||
POINT currentCursorPos{};
|
|
||||||
if (use_cursorpos_editor_startupscreen)
|
|
||||||
{
|
|
||||||
GetCursorPos(¤tCursorPos);
|
|
||||||
monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foregroundWindow = GetForegroundWindow();
|
|
||||||
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!monitor)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
winrt::com_ptr<IZoneWindow> zoneWindow;
|
|
||||||
|
|
||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
|
|
||||||
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
|
HMONITOR targetMonitor{};
|
||||||
|
|
||||||
|
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
||||||
|
if (use_cursorpos_editor_startupscreen)
|
||||||
{
|
{
|
||||||
zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, NULL);
|
POINT currentCursorPos{};
|
||||||
|
GetCursorPos(¤tCursorPos);
|
||||||
|
targetMonitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
|
targetMonitor = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTOPRIMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!zoneWindow)
|
if (!targetMonitor)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring editorLocation;
|
/*
|
||||||
|
* 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
|
||||||
|
* ...
|
||||||
|
*/
|
||||||
|
std::wstring params;
|
||||||
|
const std::wstring divider = L"/";
|
||||||
|
params += std::to_wstring(GetCurrentProcessId()) + divider; /* Process id */
|
||||||
|
|
||||||
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
|
const bool spanZonesAcrossMonitors = m_settings->GetSettings()->spanZonesAcrossMonitors;
|
||||||
|
params += std::to_wstring(spanZonesAcrossMonitors) + divider; /* Span zones */
|
||||||
|
|
||||||
|
std::vector<std::pair<HMONITOR, MONITORINFOEX>> allMonitors;
|
||||||
|
allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>();
|
||||||
|
|
||||||
|
bool showDpiWarning = false;
|
||||||
|
int prevDpiX = -1, prevDpiY = -1;
|
||||||
|
std::wstring monitorsData;
|
||||||
|
for (auto& monitor : allMonitors)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<HMONITOR, RECT>> allMonitors;
|
auto monitorId = FancyZonesUtils::GenerateMonitorId(monitor.second, monitor.first, m_currentDesktopId);
|
||||||
|
if (monitor.first == targetMonitor)
|
||||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
|
||||||
allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
|
||||||
} })
|
|
||||||
.wait();
|
|
||||||
|
|
||||||
UINT currentDpi = 0;
|
|
||||||
for (const auto& monitor : allMonitors)
|
|
||||||
{
|
{
|
||||||
|
params += *monitorId + divider; /* Monitor id where the Editor should be opened */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorId.has_value())
|
||||||
|
{
|
||||||
|
monitorsData += std::move(*monitorId) + divider; /* Monitor id */
|
||||||
|
|
||||||
UINT dpiX = 0;
|
UINT dpiX = 0;
|
||||||
UINT dpiY = 0;
|
UINT dpiY = 0;
|
||||||
if (GetDpiForMonitor(monitor.first, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
|
if (GetDpiForMonitor(monitor.first, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
|
||||||
{
|
{
|
||||||
if (currentDpi == 0)
|
monitorsData += std::to_wstring(dpiX) + divider; /* DPI */
|
||||||
|
if (spanZonesAcrossMonitors && prevDpiX != -1 && (prevDpiX != dpiX || prevDpiY != dpiY))
|
||||||
{
|
{
|
||||||
currentDpi = dpiX;
|
showDpiWarning = true;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (currentDpi != dpiX)
|
|
||||||
{
|
|
||||||
MessageBoxW(NULL,
|
|
||||||
GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
|
|
||||||
GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
|
|
||||||
MB_OK | MB_ICONWARNING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& [monitor, workArea] : allMonitors)
|
prevDpiX = dpiX;
|
||||||
{
|
prevDpiY = dpiY;
|
||||||
const auto x = workArea.left;
|
}
|
||||||
const auto y = workArea.top;
|
|
||||||
const auto width = workArea.right - workArea.left;
|
|
||||||
const auto height = workArea.bottom - workArea.top;
|
|
||||||
std::wstring editorLocationPart =
|
|
||||||
std::to_wstring(x) + L"_" +
|
|
||||||
std::to_wstring(y) + L"_" +
|
|
||||||
std::to_wstring(width) + L"_" +
|
|
||||||
std::to_wstring(height);
|
|
||||||
|
|
||||||
if (editorLocation.empty())
|
monitorsData += std::to_wstring(monitor.second.rcMonitor.left) + divider;
|
||||||
{
|
monitorsData += std::to_wstring(monitor.second.rcMonitor.top) + divider;
|
||||||
editorLocation = std::move(editorLocationPart);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
editorLocation += L'/';
|
|
||||||
editorLocation += editorLocationPart;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
params += std::to_wstring(allMonitors.size()) + divider; /* Monitors count */
|
||||||
|
params += monitorsData;
|
||||||
|
|
||||||
|
if (showDpiWarning)
|
||||||
{
|
{
|
||||||
MONITORINFOEX mi;
|
MessageBoxW(NULL,
|
||||||
mi.cbSize = sizeof(mi);
|
GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
|
||||||
|
GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
|
||||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
MB_OK | MB_ICONWARNING);
|
||||||
GetMonitorInfo(monitor, &mi);
|
|
||||||
} })
|
|
||||||
.wait();
|
|
||||||
|
|
||||||
const auto x = mi.rcWork.left;
|
|
||||||
const auto y = mi.rcWork.top;
|
|
||||||
const auto width = mi.rcWork.right - mi.rcWork.left;
|
|
||||||
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
|
||||||
editorLocation =
|
|
||||||
std::to_wstring(x) + L"_" +
|
|
||||||
std::to_wstring(y) + L"_" +
|
|
||||||
std::to_wstring(width) + L"_" +
|
|
||||||
std::to_wstring(height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& fancyZonesData = FancyZonesDataInstance();
|
const auto& fancyZonesData = FancyZonesDataInstance();
|
||||||
|
fancyZonesData.SerializeDeviceInfoToTmpFile(m_currentDesktopId);
|
||||||
if (!fancyZonesData.SerializeDeviceInfoToTmpFile(zoneWindow->UniqueId()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::wstring params =
|
|
||||||
/*1*/ editorLocation + L" " +
|
|
||||||
/*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\"";
|
|
||||||
|
|
||||||
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
||||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||||
@ -930,11 +896,11 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) n
|
|||||||
|
|
||||||
if (monitor)
|
if (monitor)
|
||||||
{
|
{
|
||||||
uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
uniqueId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uniqueId = ZoneWindowUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
|
uniqueId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring parentId{};
|
std::wstring parentId{};
|
||||||
|
@ -478,47 +478,41 @@ void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZonesData::SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const
|
void FancyZonesData::SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const
|
||||||
{
|
{
|
||||||
const auto deviceInfo = FindDeviceInfo(uniqueId);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, currentVirtualDesktop, activeZoneSetTmpFileName);
|
||||||
if (!deviceInfo.has_value())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONHelpers::DeviceInfoJSON deviceInfoJson{ uniqueId, *deviceInfo };
|
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoJson, activeZoneSetTmpFileName);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FancyZonesData::ParseDataFromTmpFiles()
|
void FancyZonesData::ParseDataFromTmpFiles()
|
||||||
{
|
{
|
||||||
ParseDeviceInfoFromTmpFile(activeZoneSetTmpFileName);
|
ParseDeviceInfoFromTmpFile(activeZoneSetTmpFileName);
|
||||||
ParseDeletedCustomZoneSetsFromTmpFile(deletedCustomZoneSetsTmpFileName);
|
ParseDeletedCustomZoneSetsFromTmpFile(deletedCustomZoneSetsTmpFileName);
|
||||||
ParseCustomZoneSetFromTmpFile(appliedZoneSetTmpFileName);
|
ParseCustomZoneSetsFromTmpFile(appliedZoneSetTmpFileName);
|
||||||
SaveFancyZonesData();
|
SaveFancyZonesData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{ dataLock };
|
std::scoped_lock lock{ dataLock };
|
||||||
const auto& deviceInfo = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
|
const auto& appliedZonesets = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
|
||||||
|
|
||||||
if (deviceInfo)
|
if (appliedZonesets)
|
||||||
{
|
{
|
||||||
deviceInfoMap[deviceInfo->deviceId] = std::move(deviceInfo->data);
|
for (const auto& zoneset : *appliedZonesets)
|
||||||
|
{
|
||||||
|
deviceInfoMap[zoneset.first] = std::move(zoneset.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FancyZonesData::ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
|
void FancyZonesData::ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{ dataLock };
|
std::scoped_lock lock{ dataLock };
|
||||||
const auto& customZoneSet = JSONHelpers::ParseCustomZoneSetFromTmpFile(tmpFilePath);
|
const auto& customZoneSets = JSONHelpers::ParseCustomZoneSetsFromTmpFile(tmpFilePath);
|
||||||
|
|
||||||
if (customZoneSet)
|
for (const auto& zoneSet : customZoneSets)
|
||||||
{
|
{
|
||||||
customZoneSetsMap[customZoneSet->uuid] = std::move(customZoneSet->data);
|
customZoneSetsMap[zoneSet.uuid] = zoneSet.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
namespace FancyZonesDataTypes
|
namespace FancyZonesDataTypes
|
||||||
{
|
{
|
||||||
struct ZoneSetData;
|
struct ZoneSetData;
|
||||||
|
struct DeviceIdData;
|
||||||
struct DeviceInfoData;
|
struct DeviceInfoData;
|
||||||
struct CustomZoneSetData;
|
struct CustomZoneSetData;
|
||||||
struct AppZoneHistoryData;
|
struct AppZoneHistoryData;
|
||||||
@ -71,7 +72,7 @@ public:
|
|||||||
|
|
||||||
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
|
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
|
||||||
|
|
||||||
bool SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const;
|
void SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const;
|
||||||
void ParseDataFromTmpFiles();
|
void ParseDataFromTmpFiles();
|
||||||
|
|
||||||
json::JsonObject GetPersistFancyZonesJSON();
|
json::JsonObject GetPersistFancyZonesJSON();
|
||||||
@ -113,7 +114,7 @@ private:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
void ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
|
void ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
void ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
void ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
|
|
||||||
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
|
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
|
||||||
|
@ -108,6 +108,15 @@ namespace FancyZonesDataTypes
|
|||||||
std::vector<size_t> zoneIndexSet;
|
std::vector<size_t> zoneIndexSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DeviceIdData
|
||||||
|
{
|
||||||
|
std::wstring deviceName;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
GUID virtualDesktopId;
|
||||||
|
std::wstring monitorId;
|
||||||
|
};
|
||||||
|
|
||||||
struct DeviceInfoData
|
struct DeviceInfoData
|
||||||
{
|
{
|
||||||
ZoneSetData activeZoneSet;
|
ZoneSetData activeZoneSet;
|
||||||
|
@ -15,12 +15,14 @@
|
|||||||
namespace NonLocalizable
|
namespace NonLocalizable
|
||||||
{
|
{
|
||||||
const wchar_t ActiveZoneSetStr[] = L"active-zoneset";
|
const wchar_t ActiveZoneSetStr[] = L"active-zoneset";
|
||||||
|
const wchar_t AppliedZonesets[] = L"applied-zonesets";
|
||||||
const wchar_t AppPathStr[] = L"app-path";
|
const wchar_t AppPathStr[] = L"app-path";
|
||||||
const wchar_t AppZoneHistoryStr[] = L"app-zone-history";
|
const wchar_t AppZoneHistoryStr[] = L"app-zone-history";
|
||||||
const wchar_t CanvasStr[] = L"canvas";
|
const wchar_t CanvasStr[] = L"canvas";
|
||||||
const wchar_t CellChildMapStr[] = L"cell-child-map";
|
const wchar_t CellChildMapStr[] = L"cell-child-map";
|
||||||
const wchar_t ColumnsPercentageStr[] = L"columns-percentage";
|
const wchar_t ColumnsPercentageStr[] = L"columns-percentage";
|
||||||
const wchar_t ColumnsStr[] = L"columns";
|
const wchar_t ColumnsStr[] = L"columns";
|
||||||
|
const wchar_t CreatedCustomZoneSets[] = L"created-custom-zone-sets";
|
||||||
const wchar_t CustomZoneSetsStr[] = L"custom-zone-sets";
|
const wchar_t CustomZoneSetsStr[] = L"custom-zone-sets";
|
||||||
const wchar_t DeletedCustomZoneSetsStr[] = L"deleted-custom-zone-sets";
|
const wchar_t DeletedCustomZoneSetsStr[] = L"deleted-custom-zone-sets";
|
||||||
const wchar_t DeviceIdStr[] = L"device-id";
|
const wchar_t DeviceIdStr[] = L"device-id";
|
||||||
@ -445,6 +447,64 @@ namespace JSONHelpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
json::JsonArray array;
|
||||||
|
for (const auto& info : deviceInfoMap)
|
||||||
|
{
|
||||||
|
JSONHelpers::DeviceInfoJSON deviceInfoJson{ info.first, info.second };
|
||||||
|
array.Append(JSONHelpers::DeviceInfoJSON::ToJson(deviceInfoJson));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop)
|
||||||
|
{
|
||||||
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
json::JsonArray array;
|
||||||
|
for (const auto& info : deviceInfoMap)
|
||||||
|
{
|
||||||
|
std::optional<FancyZonesDataTypes::DeviceIdData> id = FancyZonesUtils::ParseDeviceId(info.first);
|
||||||
|
if (id.has_value() && id->virtualDesktopId == currentVirtualDesktop)
|
||||||
|
{
|
||||||
|
JSONHelpers::DeviceInfoJSON deviceInfoJson{ info.first, info.second };
|
||||||
|
array.Append(JSONHelpers::DeviceInfoJSON::ToJson(deviceInfoJson));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TDeviceInfoMap> AppliedZonesetsJSON::FromJson(const json::JsonObject& json)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData> appliedZonesets;
|
||||||
|
|
||||||
|
auto zonesets = json.GetNamedArray(NonLocalizable::AppliedZonesets);
|
||||||
|
for (const auto& zoneset : zonesets)
|
||||||
|
{
|
||||||
|
std::optional<DeviceInfoJSON> device = DeviceInfoJSON::FromJson(zoneset.GetObjectW());
|
||||||
|
if (device.has_value())
|
||||||
|
{
|
||||||
|
appliedZonesets.insert(std::make_pair(device->deviceId, device->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return appliedZonesets;
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName)
|
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName)
|
||||||
{
|
{
|
||||||
auto result = json::from_file(zonesSettingsFileName);
|
auto result = json::from_file(zonesSettingsFileName);
|
||||||
@ -603,20 +663,34 @@ namespace JSONHelpers
|
|||||||
return customZoneSetsJSON;
|
return customZoneSetsJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath)
|
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath)
|
||||||
{
|
{
|
||||||
json::JsonObject deviceInfoJson = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
json::to_file(tmpFilePath, JSONHelpers::AppliedZonesetsJSON::ToJson(deviceInfoMap, currentVirtualDesktop));
|
||||||
json::to_file(tmpFilePath, deviceInfoJson);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
void SerializeCustomZoneSetsToTmpFile(const TCustomZoneSetsMap& customZoneSetsMap, std::wstring_view tmpFilePath)
|
||||||
{
|
{
|
||||||
std::optional<DeviceInfoJSON> result{ std::nullopt };
|
json::JsonObject result{};
|
||||||
|
|
||||||
|
json::JsonArray array;
|
||||||
|
for (const auto& zoneSet : customZoneSetsMap)
|
||||||
|
{
|
||||||
|
CustomZoneSetJSON json{ zoneSet.first, zoneSet.second };
|
||||||
|
array.Append(JSONHelpers::CustomZoneSetJSON::ToJson(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SetNamedValue(NonLocalizable::CreatedCustomZoneSets, array);
|
||||||
|
json::to_file(tmpFilePath, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TDeviceInfoMap> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
|
{
|
||||||
|
std::optional<TDeviceInfoMap> result{ std::nullopt };
|
||||||
if (std::filesystem::exists(tmpFilePath))
|
if (std::filesystem::exists(tmpFilePath))
|
||||||
{
|
{
|
||||||
if (auto zoneSetJson = json::from_file(tmpFilePath); zoneSetJson.has_value())
|
if (auto zoneSetJson = json::from_file(tmpFilePath); zoneSetJson.has_value())
|
||||||
{
|
{
|
||||||
if (auto deviceInfo = JSONHelpers::DeviceInfoJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
|
if (auto deviceInfo = JSONHelpers::AppliedZonesetsJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
|
||||||
{
|
{
|
||||||
result = std::move(deviceInfo);
|
result = std::move(deviceInfo);
|
||||||
}
|
}
|
||||||
@ -627,24 +701,27 @@ namespace JSONHelpers
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
|
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
|
||||||
{
|
{
|
||||||
std::optional<CustomZoneSetJSON> result{ std::nullopt };
|
std::vector<CustomZoneSetJSON> result;
|
||||||
if (std::filesystem::exists(tmpFilePath))
|
if (std::filesystem::exists(tmpFilePath))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (auto customZoneSetJson = json::from_file(tmpFilePath); customZoneSetJson.has_value())
|
if (auto customZoneSetJson = json::from_file(tmpFilePath); customZoneSetJson.has_value())
|
||||||
{
|
{
|
||||||
if (auto customZoneSet = JSONHelpers::CustomZoneSetJSON::FromJson(customZoneSetJson.value()); customZoneSet.has_value())
|
auto zoneSetArray = customZoneSetJson.value().GetNamedArray(NonLocalizable::CreatedCustomZoneSets);
|
||||||
|
for (const auto& zoneSet : zoneSetArray)
|
||||||
{
|
{
|
||||||
result = std::move(customZoneSet);
|
if (auto customZoneSet = JSONHelpers::CustomZoneSetJSON::FromJson(zoneSet.GetObjectW()); customZoneSet.has_value())
|
||||||
|
{
|
||||||
|
result.emplace_back(std::move(*customZoneSet));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const winrt::hresult_error&)
|
catch (const winrt::hresult_error&)
|
||||||
{
|
{
|
||||||
result = std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteTmpFile(tmpFilePath);
|
DeleteTmpFile(tmpFilePath);
|
||||||
@ -676,4 +753,4 @@ namespace JSONHelpers
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -46,7 +46,6 @@ namespace JSONHelpers
|
|||||||
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
|
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct DeviceInfoJSON
|
struct DeviceInfoJSON
|
||||||
{
|
{
|
||||||
std::wstring deviceId;
|
std::wstring deviceId;
|
||||||
@ -60,6 +59,13 @@ namespace JSONHelpers
|
|||||||
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
|
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
|
||||||
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
|
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
|
||||||
|
|
||||||
|
struct AppliedZonesetsJSON
|
||||||
|
{
|
||||||
|
static json::JsonObject ToJson(const TDeviceInfoMap& deviceInfoMap);
|
||||||
|
static json::JsonObject ToJson(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop);
|
||||||
|
static std::optional<TDeviceInfoMap> FromJson(const json::JsonObject& json);
|
||||||
|
};
|
||||||
|
|
||||||
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
|
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
|
||||||
void SaveFancyZonesData(const std::wstring& zonesSettingsFileName,
|
void SaveFancyZonesData(const std::wstring& zonesSettingsFileName,
|
||||||
const std::wstring& appZoneHistoryFileName,
|
const std::wstring& appZoneHistoryFileName,
|
||||||
@ -76,8 +82,13 @@ namespace JSONHelpers
|
|||||||
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
|
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
|
||||||
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
|
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
|
||||||
|
|
||||||
void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath);
|
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath);
|
||||||
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
std::optional<TDeviceInfoMap> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
|
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||||
|
|
||||||
|
#if defined(UNIT_TESTS)
|
||||||
|
void SerializeCustomZoneSetsToTmpFile(const TCustomZoneSetsMap& customZoneSetsMap, std::wstring_view tmpFilePath);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,44 +25,6 @@ namespace NonLocalizable
|
|||||||
|
|
||||||
using namespace FancyZonesUtils;
|
using namespace FancyZonesUtils;
|
||||||
|
|
||||||
namespace ZoneWindowUtils
|
|
||||||
{
|
|
||||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
|
|
||||||
{
|
|
||||||
MONITORINFOEXW mi;
|
|
||||||
mi.cbSize = sizeof(mi);
|
|
||||||
if (!virtualDesktopId.empty() && GetMonitorInfo(monitor, &mi))
|
|
||||||
{
|
|
||||||
Rect const monitorRect(mi.rcMonitor);
|
|
||||||
// Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
|
|
||||||
return ParseDeviceId(deviceId) +
|
|
||||||
L'_' +
|
|
||||||
std::to_wstring(monitorRect.width()) +
|
|
||||||
L'_' +
|
|
||||||
std::to_wstring(monitorRect.height()) +
|
|
||||||
L'_' +
|
|
||||||
virtualDesktopId;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId)
|
|
||||||
{
|
|
||||||
std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID };
|
|
||||||
|
|
||||||
RECT combinedResolution = GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
|
|
||||||
|
|
||||||
result += L'_';
|
|
||||||
result += std::to_wstring(combinedResolution.right - combinedResolution.left);
|
|
||||||
result += L'_';
|
|
||||||
result += std::to_wstring(combinedResolution.bottom - combinedResolution.top);
|
|
||||||
result += L'_';
|
|
||||||
result += virtualDesktopId;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
|
struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -2,12 +2,6 @@
|
|||||||
#include "FancyZones.h"
|
#include "FancyZones.h"
|
||||||
#include "lib/ZoneSet.h"
|
#include "lib/ZoneSet.h"
|
||||||
|
|
||||||
namespace ZoneWindowUtils
|
|
||||||
{
|
|
||||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
|
|
||||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing single work area, which is defined by monitor and virtual desktop.
|
* Class representing single work area, which is defined by monitor and virtual desktop.
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
|
|
||||||
|
#include <fancyzones/lib/FancyZonesDataTypes.h>
|
||||||
|
|
||||||
// Non-Localizable strings
|
// Non-Localizable strings
|
||||||
namespace NonLocalizable
|
namespace NonLocalizable
|
||||||
{
|
{
|
||||||
@ -40,7 +42,7 @@ namespace
|
|||||||
|
|
||||||
namespace FancyZonesUtils
|
namespace FancyZonesUtils
|
||||||
{
|
{
|
||||||
std::wstring ParseDeviceId(const std::wstring& deviceId)
|
std::wstring TrimDeviceId(const std::wstring& deviceId)
|
||||||
{
|
{
|
||||||
// We're interested in the unique part between the first and last #'s
|
// We're interested in the unique part between the first and last #'s
|
||||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||||
@ -63,7 +65,95 @@ namespace FancyZonesUtils
|
|||||||
return defaultDeviceId;
|
return defaultDeviceId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& str)
|
||||||
|
{
|
||||||
|
FancyZonesDataTypes::DeviceIdData data;
|
||||||
|
|
||||||
|
std::wstring temp;
|
||||||
|
std::wstringstream wss(str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Important fix for device info that contains a '_' in the name:
|
||||||
|
1. first search for '#'
|
||||||
|
2. Then split the remaining string by '_'
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Step 1: parse the name until the #, then to the '_'
|
||||||
|
if (str.find(L'#') != std::string::npos)
|
||||||
|
{
|
||||||
|
std::getline(wss, temp, L'#');
|
||||||
|
|
||||||
|
data.deviceName = temp;
|
||||||
|
|
||||||
|
if (!std::getline(wss, temp, L'_'))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.deviceName += L"#" + temp;
|
||||||
|
}
|
||||||
|
else if(std::getline(wss, temp, L'_') && !temp.empty())
|
||||||
|
{
|
||||||
|
data.deviceName = temp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: parse the rest of the id
|
||||||
|
std::vector<std::wstring> parts;
|
||||||
|
while (std::getline(wss, temp, L'_'))
|
||||||
|
{
|
||||||
|
parts.push_back(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.size() != 3 && parts.size() != 4)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||||
|
1. monitor id [string]
|
||||||
|
2. width of device [int]
|
||||||
|
3. height of device [int]
|
||||||
|
4. virtual desktop id (GUID) [string]
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (const auto& c : parts[0])
|
||||||
|
{
|
||||||
|
std::stoi(std::wstring(&c));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& c : parts[1])
|
||||||
|
{
|
||||||
|
std::stoi(std::wstring(&c));
|
||||||
|
}
|
||||||
|
|
||||||
|
data.width = std::stoi(parts[0]);
|
||||||
|
data.height = std::stoi(parts[1]);
|
||||||
|
}
|
||||||
|
catch (const std::exception&)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.size() == 4)
|
||||||
|
{
|
||||||
|
data.monitorId = parts[3]; //could be empty
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||||
{
|
{
|
||||||
@ -457,6 +547,71 @@ namespace FancyZonesUtils
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
|
||||||
|
{
|
||||||
|
MONITORINFOEXW mi;
|
||||||
|
mi.cbSize = sizeof(mi);
|
||||||
|
if (!virtualDesktopId.empty() && GetMonitorInfo(monitor, &mi))
|
||||||
|
{
|
||||||
|
Rect const monitorRect(mi.rcMonitor);
|
||||||
|
// Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
|
||||||
|
return TrimDeviceId(deviceId) +
|
||||||
|
L'_' +
|
||||||
|
std::to_wstring(monitorRect.width()) +
|
||||||
|
L'_' +
|
||||||
|
std::to_wstring(monitorRect.height()) +
|
||||||
|
L'_' +
|
||||||
|
virtualDesktopId;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId)
|
||||||
|
{
|
||||||
|
std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID };
|
||||||
|
|
||||||
|
RECT combinedResolution = GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
|
||||||
|
|
||||||
|
result += L'_';
|
||||||
|
result += std::to_wstring(combinedResolution.right - combinedResolution.left);
|
||||||
|
result += L'_';
|
||||||
|
result += std::to_wstring(combinedResolution.bottom - combinedResolution.top);
|
||||||
|
result += L'_';
|
||||||
|
result += virtualDesktopId;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& virtualDesktopId)
|
||||||
|
{
|
||||||
|
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
||||||
|
PCWSTR deviceId = nullptr;
|
||||||
|
|
||||||
|
bool validMonitor = true;
|
||||||
|
if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1))
|
||||||
|
{
|
||||||
|
if (displayDevice.DeviceID[0] != L'\0')
|
||||||
|
{
|
||||||
|
deviceId = displayDevice.DeviceID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceId)
|
||||||
|
{
|
||||||
|
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
||||||
|
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
||||||
|
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
||||||
|
}
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string vdId;
|
||||||
|
if (SUCCEEDED(StringFromCLSID(virtualDesktopId, &vdId)))
|
||||||
|
{
|
||||||
|
return GenerateUniqueId(monitor, deviceId, vdId.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
||||||
{
|
{
|
||||||
using complex = std::complex<double>;
|
using complex = std::complex<double>;
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
#include "gdiplus.h"
|
#include "gdiplus.h"
|
||||||
#include <common/string_utils.h>
|
#include <common/string_utils.h>
|
||||||
|
|
||||||
|
namespace FancyZonesDataTypes
|
||||||
|
{
|
||||||
|
struct DeviceIdData;
|
||||||
|
}
|
||||||
|
|
||||||
namespace FancyZonesUtils
|
namespace FancyZonesUtils
|
||||||
{
|
{
|
||||||
struct Rect
|
struct Rect
|
||||||
@ -131,6 +136,28 @@ namespace FancyZonesUtils
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<RECT MONITORINFO::*member>
|
||||||
|
std::vector<std::pair<HMONITOR, MONITORINFOEX>> GetAllMonitorInfo()
|
||||||
|
{
|
||||||
|
using result_t = std::vector<std::pair<HMONITOR, MONITORINFOEX>>;
|
||||||
|
result_t result;
|
||||||
|
|
||||||
|
auto enumMonitors = [](HMONITOR monitor, HDC hdc, LPRECT pRect, LPARAM param) -> BOOL {
|
||||||
|
MONITORINFOEX mi;
|
||||||
|
mi.cbSize = sizeof(mi);
|
||||||
|
result_t& result = *reinterpret_cast<result_t*>(param);
|
||||||
|
if (GetMonitorInfo(monitor, &mi))
|
||||||
|
{
|
||||||
|
result.push_back({ monitor, mi });
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
};
|
||||||
|
|
||||||
|
EnumDisplayMonitors(NULL, NULL, enumMonitors, reinterpret_cast<LPARAM>(&result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template<RECT MONITORINFO::*member>
|
template<RECT MONITORINFO::*member>
|
||||||
RECT GetAllMonitorsCombinedRect()
|
RECT GetAllMonitorsCombinedRect()
|
||||||
{
|
{
|
||||||
@ -157,8 +184,6 @@ namespace FancyZonesUtils
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring ParseDeviceId(const std::wstring& deviceId);
|
|
||||||
|
|
||||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||||
@ -174,6 +199,13 @@ namespace FancyZonesUtils
|
|||||||
void RestoreWindowOrigin(HWND window) noexcept;
|
void RestoreWindowOrigin(HWND window) noexcept;
|
||||||
|
|
||||||
bool IsValidGuid(const std::wstring& str);
|
bool IsValidGuid(const std::wstring& str);
|
||||||
|
|
||||||
|
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
|
||||||
|
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
|
||||||
|
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& virtualDesktopId);
|
||||||
|
|
||||||
|
std::wstring TrimDeviceId(const std::wstring& deviceId);
|
||||||
|
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& deviceId);
|
||||||
bool IsValidDeviceId(const std::wstring& str);
|
bool IsValidDeviceId(const std::wstring& str);
|
||||||
|
|
||||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
||||||
|
@ -938,6 +938,92 @@ namespace FancyZonesUnitTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_CLASS(AppliedZonesetsUnitTests)
|
||||||
|
{
|
||||||
|
TEST_METHOD(SingleDevice)
|
||||||
|
{
|
||||||
|
const std::wstring deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||||
|
const std::wstring zoneUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
|
||||||
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
|
DeviceInfoData data{ ZoneSetData{ zoneUuid, type }, true, 10, 4 };
|
||||||
|
|
||||||
|
TDeviceInfoMap expected;
|
||||||
|
expected.insert(std::make_pair(deviceId, data));
|
||||||
|
|
||||||
|
json::JsonObject json = AppliedZonesetsJSON::ToJson(expected);
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
Assert::AreEqual(expected.size(), actual->size());
|
||||||
|
for (const auto& exp : expected)
|
||||||
|
{
|
||||||
|
Assert::IsTrue(actual->contains(exp.first));
|
||||||
|
|
||||||
|
const auto act = actual->find(exp.first);
|
||||||
|
Assert::AreEqual(exp.second.zoneCount, act->second.zoneCount);
|
||||||
|
Assert::AreEqual(exp.second.showSpacing, act->second.showSpacing);
|
||||||
|
Assert::AreEqual(exp.second.spacing, act->second.spacing);
|
||||||
|
Assert::AreEqual(exp.second.activeZoneSet.uuid, act->second.activeZoneSet.uuid);
|
||||||
|
Assert::AreEqual((int)exp.second.activeZoneSet.type, (int)act->second.activeZoneSet.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (MultipleDevices)
|
||||||
|
{
|
||||||
|
TDeviceInfoMap expected;
|
||||||
|
expected.insert(std::make_pair(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Columns }, true, 10, 4 }));
|
||||||
|
expected.insert(std::make_pair(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1538}", DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502905}", ZoneSetLayoutType::Rows }, false, 8, 5 }));
|
||||||
|
expected.insert(std::make_pair(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1537}", DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502904}", ZoneSetLayoutType::Grid }, true, 9, 6 }));
|
||||||
|
|
||||||
|
json::JsonObject json = AppliedZonesetsJSON::ToJson(expected);
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
Assert::AreEqual(expected.size(), actual->size());
|
||||||
|
for (const auto& exp : expected)
|
||||||
|
{
|
||||||
|
Assert::IsTrue(actual->contains(exp.first));
|
||||||
|
|
||||||
|
const auto act = actual->find(exp.first);
|
||||||
|
Assert::AreEqual(exp.second.zoneCount, act->second.zoneCount);
|
||||||
|
Assert::AreEqual(exp.second.showSpacing, act->second.showSpacing);
|
||||||
|
Assert::AreEqual(exp.second.spacing, act->second.spacing);
|
||||||
|
Assert::AreEqual(exp.second.activeZoneSet.uuid, act->second.activeZoneSet.uuid);
|
||||||
|
Assert::AreEqual((int)exp.second.activeZoneSet.type, (int)act->second.activeZoneSet.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (FromJsonNoDeviceId)
|
||||||
|
{
|
||||||
|
json::JsonObject json = json::JsonObject::Parse(L"{\"applied-zonesets\": [{\"device-id\": \"\",\"active-zoneset\": {\"uuid\": \"{81B9FCD3-88CA-4B21-A681-5D1129A1527F}\",\"type\": \"grid\"},\"editor-show-spacing\": true,\"editor-spacing\": 5,\"editor-zone-count\": 4},{\"device-id\": \"\",\"active-zoneset\": {\"uuid\": \"{8110E0D5-4815-4A35-A5AC-DF82A65FF58B}\",\"type\": \"priority-grid\"},\"editor-show-spacing\": false,\"editor-spacing\": 6,\"editor-zone-count\": 2}]}");
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
Assert::IsTrue(actual->empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (FromInvalidJsonNotArray)
|
||||||
|
{
|
||||||
|
json::JsonObject json = json::JsonObject::Parse(L"{\"applied-zonesets\": {\"device-id\": \"\",\"active-zoneset\": {\"uuid\": \"{81B9FCD3-88CA-4B21-A681-5D1129A1527F}\",\"type\": \"grid\"},\"editor-show-spacing\": true,\"editor-spacing\": 5,\"editor-zone-count\": 4}}");
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (FromEmptyJson)
|
||||||
|
{
|
||||||
|
json::JsonObject json = json::JsonObject::Parse(L"{}");
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (FromEmptyDeviceArray)
|
||||||
|
{
|
||||||
|
json::JsonObject json = json::JsonObject::Parse(L"{\"applied-zonesets\": []}");
|
||||||
|
auto actual = AppliedZonesetsJSON::FromJson(json);
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
Assert::IsTrue(actual->empty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TEST_CLASS (FancyZonesDataUnitTests)
|
TEST_CLASS (FancyZonesDataUnitTests)
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -947,6 +1033,8 @@ namespace FancyZonesUnitTests
|
|||||||
const json::JsonValue m_defaultCustomDeviceValue = json::JsonValue::Parse(m_defaultCustomDeviceStr);
|
const json::JsonValue m_defaultCustomDeviceValue = json::JsonValue::Parse(m_defaultCustomDeviceStr);
|
||||||
const json::JsonObject m_defaultCustomDeviceObj = json::JsonObject::Parse(m_defaultCustomDeviceStr);
|
const json::JsonObject m_defaultCustomDeviceObj = json::JsonObject::Parse(m_defaultCustomDeviceStr);
|
||||||
|
|
||||||
|
GUID m_defaultVDId;
|
||||||
|
|
||||||
HINSTANCE m_hInst{};
|
HINSTANCE m_hInst{};
|
||||||
FancyZonesData& m_fzData = FancyZonesDataInstance();
|
FancyZonesData& m_fzData = FancyZonesDataInstance();
|
||||||
|
|
||||||
@ -964,6 +1052,10 @@ namespace FancyZonesUnitTests
|
|||||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||||
m_fzData.clear_data();
|
m_fzData.clear_data();
|
||||||
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName));
|
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName));
|
||||||
|
|
||||||
|
auto guid = Helpers::StringToGuid(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}");
|
||||||
|
Assert::IsTrue(guid.has_value());
|
||||||
|
m_defaultVDId = *guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_METHOD_CLEANUP(CleanUp)
|
TEST_METHOD_CLEANUP(CleanUp)
|
||||||
@ -1098,15 +1190,18 @@ namespace FancyZonesUnitTests
|
|||||||
{
|
{
|
||||||
FancyZonesData data;
|
FancyZonesData data;
|
||||||
data.SetSettingsModulePath(m_moduleName);
|
data.SetSettingsModulePath(m_moduleName);
|
||||||
DeviceInfoJSON deviceInfo{ L"default_device_id", DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
|
||||||
|
TDeviceInfoMap deviceInfoMap;
|
||||||
|
DeviceInfoData deviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 };
|
||||||
|
deviceInfoMap.insert(std::make_pair(m_defaultDeviceId, deviceInfoData));
|
||||||
|
|
||||||
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, path);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_defaultVDId, path);
|
||||||
|
|
||||||
bool actualFileExists = std::filesystem::exists(path);
|
bool actualFileExists = std::filesystem::exists(path);
|
||||||
Assert::IsTrue(actualFileExists);
|
Assert::IsTrue(actualFileExists);
|
||||||
|
|
||||||
auto expectedData = DeviceInfoJSON::ToJson(deviceInfo);
|
auto expectedData = AppliedZonesetsJSON::ToJson(deviceInfoMap);
|
||||||
auto actualSavedData = json::from_file(path);
|
auto actualSavedData = json::from_file(path);
|
||||||
std::filesystem::remove(path); //clean up before compare asserts
|
std::filesystem::remove(path); //clean up before compare asserts
|
||||||
|
|
||||||
@ -1118,10 +1213,15 @@ namespace FancyZonesUnitTests
|
|||||||
{
|
{
|
||||||
FancyZonesData data;
|
FancyZonesData data;
|
||||||
data.SetSettingsModulePath(m_moduleName);
|
data.SetSettingsModulePath(m_moduleName);
|
||||||
|
|
||||||
const std::wstring deviceId = m_defaultDeviceId;
|
const std::wstring deviceId = m_defaultDeviceId;
|
||||||
DeviceInfoJSON expected{ deviceId, DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
DeviceInfoData expected{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 };
|
||||||
|
|
||||||
|
TDeviceInfoMap expectedDeviceInfoMap;
|
||||||
|
expectedDeviceInfoMap.insert(std::make_pair(deviceId, expected));
|
||||||
|
|
||||||
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(expected, path);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(expectedDeviceInfoMap, m_defaultVDId, path);
|
||||||
|
|
||||||
data.ParseDeviceInfoFromTmpFile(path);
|
data.ParseDeviceInfoFromTmpFile(path);
|
||||||
|
|
||||||
@ -1136,11 +1236,11 @@ namespace FancyZonesUnitTests
|
|||||||
Assert::AreEqual((size_t)1, devices.size());
|
Assert::AreEqual((size_t)1, devices.size());
|
||||||
|
|
||||||
auto actual = devices.find(deviceId)->second;
|
auto actual = devices.find(deviceId)->second;
|
||||||
Assert::AreEqual(expected.data.showSpacing, actual.showSpacing);
|
Assert::AreEqual(expected.showSpacing, actual.showSpacing);
|
||||||
Assert::AreEqual(expected.data.spacing, actual.spacing);
|
Assert::AreEqual(expected.spacing, actual.spacing);
|
||||||
Assert::AreEqual(expected.data.zoneCount, actual.zoneCount);
|
Assert::AreEqual(expected.zoneCount, actual.zoneCount);
|
||||||
Assert::AreEqual((int)expected.data.activeZoneSet.type, (int)actual.activeZoneSet.type);
|
Assert::AreEqual((int)expected.activeZoneSet.type, (int)actual.activeZoneSet.type);
|
||||||
Assert::AreEqual(expected.data.activeZoneSet.uuid.c_str(), actual.activeZoneSet.uuid.c_str());
|
Assert::AreEqual(expected.activeZoneSet.uuid.c_str(), actual.activeZoneSet.uuid.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_METHOD (DeviceInfoReadTempNonexistent)
|
TEST_METHOD (DeviceInfoReadTempNonexistent)
|
||||||
@ -1525,9 +1625,12 @@ namespace FancyZonesUnitTests
|
|||||||
const std::wstring deviceId = L"default_device_id";
|
const std::wstring deviceId = L"default_device_id";
|
||||||
|
|
||||||
{
|
{
|
||||||
DeviceInfoJSON deviceInfo{ deviceId, DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
TDeviceInfoMap deviceInfoMap;
|
||||||
|
DeviceInfoData deviceInfoData { ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 };
|
||||||
|
deviceInfoMap.insert(std::make_pair(deviceId, deviceInfoData));
|
||||||
|
|
||||||
const std::wstring deviceInfoPath = m_fzData.zonesSettingsFileName + L".device_info_tmp";
|
const std::wstring deviceInfoPath = m_fzData.zonesSettingsFileName + L".device_info_tmp";
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_defaultVDId, deviceInfoPath);
|
||||||
|
|
||||||
m_fzData.ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
m_fzData.ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
||||||
std::filesystem::remove(deviceInfoPath);
|
std::filesystem::remove(deviceInfoPath);
|
||||||
@ -1540,13 +1643,18 @@ namespace FancyZonesUnitTests
|
|||||||
.rowsPercents = { 10000 },
|
.rowsPercents = { 10000 },
|
||||||
.columnsPercents = { 2500, 5000, 2500 },
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
.cellChildMap = { { 0, 1, 2 } } }));
|
.cellChildMap = { { 0, 1, 2 } } }));
|
||||||
CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } };
|
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Grid, grid };
|
||||||
|
CustomZoneSetJSON expected{ uuid, zoneSetData };
|
||||||
|
|
||||||
FancyZonesData data;
|
FancyZonesData data;
|
||||||
data.SetSettingsModulePath(m_moduleName);
|
data.SetSettingsModulePath(m_moduleName);
|
||||||
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
|
||||||
json::to_file(path, CustomZoneSetJSON::ToJson(expected));
|
|
||||||
m_fzData.ParseCustomZoneSetFromTmpFile(path);
|
TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(uuid, zoneSetData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, path);
|
||||||
|
|
||||||
|
m_fzData.ParseCustomZoneSetsFromTmpFile(path);
|
||||||
|
|
||||||
bool actualFileExists = std::filesystem::exists(path);
|
bool actualFileExists = std::filesystem::exists(path);
|
||||||
if (actualFileExists)
|
if (actualFileExists)
|
||||||
@ -1572,7 +1680,7 @@ namespace FancyZonesUnitTests
|
|||||||
const std::wstring path = m_fzData.zonesSettingsFileName + L".test_tmp";
|
const std::wstring path = m_fzData.zonesSettingsFileName + L".test_tmp";
|
||||||
const std::wstring deviceId = L"default_device_id";
|
const std::wstring deviceId = L"default_device_id";
|
||||||
|
|
||||||
m_fzData.ParseCustomZoneSetFromTmpFile(path);
|
m_fzData.ParseCustomZoneSetsFromTmpFile(path);
|
||||||
auto devices = m_fzData.GetDeviceInfoMap();
|
auto devices = m_fzData.GetDeviceInfoMap();
|
||||||
Assert::AreEqual((size_t)0, devices.size());
|
Assert::AreEqual((size_t)0, devices.size());
|
||||||
}
|
}
|
||||||
|
@ -40,28 +40,143 @@ namespace FancyZonesUnitTests
|
|||||||
|
|
||||||
TEST_CLASS(UtilUnitTests)
|
TEST_CLASS(UtilUnitTests)
|
||||||
{
|
{
|
||||||
TEST_METHOD(TestParseDeviceId)
|
TEST_METHOD (TestTrimDeviceId)
|
||||||
{
|
{
|
||||||
// We're interested in the unique part between the first and last #'s
|
// We're interested in the unique part between the first and last #'s
|
||||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||||
const std::wstring input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
const std::wstring input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
||||||
const std::wstring actual = ParseDeviceId(input);
|
const std::wstring actual = TrimDeviceId(input);
|
||||||
const std::wstring expected = L"DELA026#5&10a58c63&0&UID16777488";
|
const std::wstring expected = L"DELA026#5&10a58c63&0&UID16777488";
|
||||||
Assert::AreEqual(expected, actual);
|
Assert::AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_METHOD(TestParseInvalidDeviceId)
|
TEST_METHOD(TestTrimInvalidDeviceId)
|
||||||
{
|
{
|
||||||
// We're interested in the unique part between the first and last #'s
|
// We're interested in the unique part between the first and last #'s
|
||||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||||
const std::wstring input = L"AnInvalidDeviceId";
|
const std::wstring input = L"AnInvalidDeviceId";
|
||||||
const std::wstring actual = ParseDeviceId(input);
|
const std::wstring actual = TrimDeviceId(input);
|
||||||
const std::wstring expected = L"FallbackDevice";
|
const std::wstring expected = L"FallbackDevice";
|
||||||
Assert::AreEqual(expected, actual);
|
Assert::AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestParseDeviceId01)
|
||||||
|
{
|
||||||
|
const std::wstring input = L"AOC0001#5&37ac4db&0&UID160002_1536_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
|
||||||
|
GUID guid;
|
||||||
|
const auto expectedGuidStr = L"{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
CLSIDFromString(expectedGuidStr, &guid);
|
||||||
|
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid };
|
||||||
|
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
|
||||||
|
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||||
|
Assert::AreEqual(expected.height, actual->height);
|
||||||
|
Assert::AreEqual(expected.width, actual->width);
|
||||||
|
Assert::AreEqual(expected.monitorId, actual->monitorId);
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string actualGuidStr;
|
||||||
|
StringFromCLSID(actual->virtualDesktopId, &actualGuidStr);
|
||||||
|
Assert::AreEqual(expectedGuidStr, actualGuidStr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceId02)
|
||||||
|
{
|
||||||
|
const std::wstring input = L"AOC0001#5&37ac4db&0&UID160002_1536_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}_monitorId";
|
||||||
|
|
||||||
|
GUID guid;
|
||||||
|
const auto expectedGuidStr = L"{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
CLSIDFromString(expectedGuidStr, &guid);
|
||||||
|
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid, L"monitorId" };
|
||||||
|
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
|
||||||
|
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||||
|
Assert::AreEqual(expected.height, actual->height);
|
||||||
|
Assert::AreEqual(expected.width, actual->width);
|
||||||
|
Assert::AreEqual(expected.monitorId, actual->monitorId);
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string actualGuidStr;
|
||||||
|
StringFromCLSID(actual->virtualDesktopId, &actualGuidStr);
|
||||||
|
Assert::AreEqual(expectedGuidStr, actualGuidStr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceId03)
|
||||||
|
{
|
||||||
|
// difference with previous tests is in the device name: there is no # symbol
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
|
||||||
|
GUID guid;
|
||||||
|
const auto expectedGuidStr = L"{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
CLSIDFromString(expectedGuidStr, &guid);
|
||||||
|
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC00015&37ac4db&0&UID160002", 1536, 960, guid };
|
||||||
|
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsTrue(actual.has_value());
|
||||||
|
|
||||||
|
Assert::AreEqual(expected.deviceName, actual->deviceName);
|
||||||
|
Assert::AreEqual(expected.height, actual->height);
|
||||||
|
Assert::AreEqual(expected.width, actual->width);
|
||||||
|
Assert::AreEqual(expected.monitorId, actual->monitorId);
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string actualGuidStr;
|
||||||
|
StringFromCLSID(actual->virtualDesktopId, &actualGuidStr);
|
||||||
|
Assert::AreEqual(expectedGuidStr, actualGuidStr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid01)
|
||||||
|
{
|
||||||
|
// no width or height
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid02)
|
||||||
|
{
|
||||||
|
// no width and height
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_{E0A2904E-889C-4532-95B1-28FE15C16F66}_monitorId";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid03)
|
||||||
|
{
|
||||||
|
// no guid
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid04)
|
||||||
|
{
|
||||||
|
// invalid guid
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{asdf}";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid05)
|
||||||
|
{
|
||||||
|
// invalid width/height
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (TestParseDeviceIdInvalid06)
|
||||||
|
{
|
||||||
|
// changed order
|
||||||
|
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_monitorId_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
|
||||||
|
const auto actual = ParseDeviceId(input);
|
||||||
|
Assert::IsFalse(actual.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_METHOD(TestMonitorOrdering01)
|
TEST_METHOD(TestMonitorOrdering01)
|
||||||
{
|
{
|
||||||
// Three horizontally arranged monitors, bottom aligned, with increasing sizes
|
// Three horizontally arranged monitors, bottom aligned, with increasing sizes
|
||||||
|
@ -172,3 +172,14 @@ std::wstring Helpers::CreateGuidString()
|
|||||||
|
|
||||||
return L"";
|
return L"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<GUID> Helpers::StringToGuid(const std::wstring& str)
|
||||||
|
{
|
||||||
|
GUID guid;
|
||||||
|
if (CLSIDFromString(str.c_str(), &guid) == S_OK)
|
||||||
|
{
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
@ -57,6 +57,7 @@ namespace Helpers
|
|||||||
{
|
{
|
||||||
std::wstring GuidToString(const GUID& guid);
|
std::wstring GuidToString(const GUID& guid);
|
||||||
std::wstring CreateGuidString();
|
std::wstring CreateGuidString();
|
||||||
|
std::optional<GUID> StringToGuid(const std::wstring& str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "lib\FancyZonesData.h"
|
#include "lib\FancyZonesData.h"
|
||||||
#include "lib\FancyZonesDataTypes.h"
|
#include "lib\FancyZonesDataTypes.h"
|
||||||
#include "lib\JsonHelpers.h"
|
#include "lib\JsonHelpers.h"
|
||||||
|
#include "lib\VirtualDesktopUtils.h"
|
||||||
#include "lib\ZoneSet.h"
|
#include "lib\ZoneSet.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -1048,9 +1049,14 @@ namespace FancyZonesUnitTests
|
|||||||
//prepare device data
|
//prepare device data
|
||||||
{
|
{
|
||||||
const std::wstring zoneUuid = L"default_device_id";
|
const std::wstring zoneUuid = L"default_device_id";
|
||||||
JSONHelpers::DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
|
||||||
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
|
deviceInfoMap.insert(std::make_pair(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 }));
|
||||||
|
|
||||||
|
GUID virtualDesktopId{};
|
||||||
|
Assert::IsTrue(VirtualDesktopUtils::GetCurrentVirtualDesktopId(&virtualDesktopId), L"Cannot create virtual desktop id");
|
||||||
const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
|
const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, virtualDesktopId, deviceInfoPath);
|
||||||
|
|
||||||
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
||||||
std::filesystem::remove(deviceInfoPath);
|
std::filesystem::remove(deviceInfoPath);
|
||||||
@ -1060,10 +1066,14 @@ namespace FancyZonesUnitTests
|
|||||||
wil::unique_cotaskmem_string uuid;
|
wil::unique_cotaskmem_string uuid;
|
||||||
Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid));
|
Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid));
|
||||||
const CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 0, 0, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } };
|
const CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 0, 0, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } };
|
||||||
JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info } };
|
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
|
JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), zoneSetData };
|
||||||
|
JSONHelpers::TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(uuid.get(), zoneSetData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, m_path);
|
||||||
|
|
||||||
Assert::IsTrue(std::filesystem::exists(m_path));
|
Assert::IsTrue(std::filesystem::exists(m_path));
|
||||||
FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path);
|
FancyZonesDataInstance().ParseCustomZoneSetsFromTmpFile(m_path);
|
||||||
|
|
||||||
//test
|
//test
|
||||||
const int spacing = 10;
|
const int spacing = 10;
|
||||||
@ -1083,9 +1093,14 @@ namespace FancyZonesUnitTests
|
|||||||
//prepare device data
|
//prepare device data
|
||||||
{
|
{
|
||||||
const std::wstring zoneUuid = L"default_device_id";
|
const std::wstring zoneUuid = L"default_device_id";
|
||||||
JSONHelpers::DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
|
|
||||||
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
|
deviceInfoMap.insert(std::make_pair(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 }));
|
||||||
|
|
||||||
|
GUID virtualDesktopId{};
|
||||||
|
Assert::IsTrue(VirtualDesktopUtils::GetCurrentVirtualDesktopId(&virtualDesktopId), L"Cannot create virtual desktop id");
|
||||||
const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
|
const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
|
||||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, virtualDesktopId, deviceInfoPath);
|
||||||
|
|
||||||
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
|
||||||
std::filesystem::remove(deviceInfoPath);
|
std::filesystem::remove(deviceInfoPath);
|
||||||
@ -1100,10 +1115,14 @@ namespace FancyZonesUnitTests
|
|||||||
.rowsPercents = { 10000 },
|
.rowsPercents = { 10000 },
|
||||||
.columnsPercents = { 2500, 5000, 2500 },
|
.columnsPercents = { 2500, 5000, 2500 },
|
||||||
.cellChildMap = { { 0, 1, 2 } } }));
|
.cellChildMap = { { 0, 1, 2 } } }));
|
||||||
JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } };
|
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Grid, grid };
|
||||||
json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
|
JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), zoneSetData };
|
||||||
|
JSONHelpers::TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(uuid.get(), zoneSetData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, m_path);
|
||||||
|
|
||||||
Assert::IsTrue(std::filesystem::exists(m_path));
|
Assert::IsTrue(std::filesystem::exists(m_path));
|
||||||
FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path);
|
FancyZonesDataInstance().ParseCustomZoneSetsFromTmpFile(m_path);
|
||||||
|
|
||||||
const int spacing = 10;
|
const int spacing = 10;
|
||||||
const int zoneCount = grid.rows() * grid.columns();
|
const int zoneCount = grid.rows() * grid.columns();
|
||||||
|
@ -69,7 +69,8 @@ namespace FancyZonesUnitTests
|
|||||||
|
|
||||||
HINSTANCE m_hInst{};
|
HINSTANCE m_hInst{};
|
||||||
HMONITOR m_monitor{};
|
HMONITOR m_monitor{};
|
||||||
MONITORINFO m_monitorInfo{};
|
MONITORINFOEX m_monitorInfo{};
|
||||||
|
GUID m_virtualDesktopGuid{};
|
||||||
|
|
||||||
FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
|
FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
|
||||||
|
|
||||||
@ -102,6 +103,10 @@ namespace FancyZonesUnitTests
|
|||||||
|
|
||||||
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
|
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
|
||||||
m_fancyZonesData.clear_data();
|
m_fancyZonesData.clear_data();
|
||||||
|
|
||||||
|
auto guid = Helpers::StringToGuid(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}");
|
||||||
|
Assert::IsTrue(guid.has_value());
|
||||||
|
m_virtualDesktopGuid = *guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_METHOD_CLEANUP(Cleanup)
|
TEST_METHOD_CLEANUP(Cleanup)
|
||||||
@ -154,7 +159,7 @@ namespace FancyZonesUnitTests
|
|||||||
TEST_METHOD(CreateZoneWindowNoDeviceId)
|
TEST_METHOD(CreateZoneWindowNoDeviceId)
|
||||||
{
|
{
|
||||||
// Generate unique id without device id
|
// Generate unique id without device id
|
||||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, {}, m_virtualDesktopId);
|
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, {}, m_virtualDesktopId);
|
||||||
auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {});
|
auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {});
|
||||||
|
|
||||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||||
@ -172,7 +177,7 @@ namespace FancyZonesUnitTests
|
|||||||
TEST_METHOD(CreateZoneWindowNoDesktopId)
|
TEST_METHOD(CreateZoneWindowNoDesktopId)
|
||||||
{
|
{
|
||||||
// Generate unique id without virtual desktop id
|
// Generate unique id without virtual desktop id
|
||||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId, {});
|
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, m_deviceId, {});
|
||||||
auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {});
|
auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {});
|
||||||
|
|
||||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||||
@ -219,9 +224,9 @@ namespace FancyZonesUnitTests
|
|||||||
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
|
||||||
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), type };
|
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), type };
|
||||||
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
|
||||||
json::to_file(activeZoneSetTempPath, json);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
|
||||||
|
|
||||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
|
|
||||||
@ -248,18 +253,20 @@ namespace FancyZonesUnitTests
|
|||||||
const auto customSetGuid = Helpers::CreateGuidString();
|
const auto customSetGuid = Helpers::CreateGuidString();
|
||||||
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
|
||||||
json::to_file(activeZoneSetTempPath, json);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
|
||||||
|
|
||||||
const auto info = CanvasLayoutInfo{
|
const auto info = CanvasLayoutInfo{
|
||||||
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
};
|
};
|
||||||
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData });
|
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData });
|
||||||
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
JSONHelpers::TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
|
||||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
//temp file read on initialization
|
//temp file read on initialization
|
||||||
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
||||||
@ -285,9 +292,9 @@ namespace FancyZonesUnitTests
|
|||||||
const auto customSetGuid = Helpers::CreateGuidString();
|
const auto customSetGuid = Helpers::CreateGuidString();
|
||||||
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
|
||||||
json::to_file(activeZoneSetTempPath, json);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
|
||||||
|
|
||||||
const auto info = CanvasLayoutInfo{
|
const auto info = CanvasLayoutInfo{
|
||||||
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
@ -295,7 +302,9 @@ namespace FancyZonesUnitTests
|
|||||||
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
|
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
|
||||||
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
|
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
|
||||||
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
JSONHelpers::TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
|
||||||
|
|
||||||
//save same zone as deleted
|
//save same zone as deleted
|
||||||
json::JsonObject deletedCustomZoneSets = {};
|
json::JsonObject deletedCustomZoneSets = {};
|
||||||
@ -306,7 +315,7 @@ namespace FancyZonesUnitTests
|
|||||||
|
|
||||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
||||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
//temp file read on initialization
|
//temp file read on initialization
|
||||||
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
||||||
@ -331,9 +340,9 @@ namespace FancyZonesUnitTests
|
|||||||
const auto customSetGuid = Helpers::CreateGuidString();
|
const auto customSetGuid = Helpers::CreateGuidString();
|
||||||
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
|
||||||
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
|
||||||
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
|
JSONHelpers::TDeviceInfoMap deviceInfoMap;
|
||||||
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
|
||||||
json::to_file(activeZoneSetTempPath, json);
|
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
|
||||||
|
|
||||||
const auto info = CanvasLayoutInfo{
|
const auto info = CanvasLayoutInfo{
|
||||||
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
|
||||||
@ -341,7 +350,9 @@ namespace FancyZonesUnitTests
|
|||||||
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
|
||||||
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
|
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
|
||||||
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
|
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
|
||||||
json::to_file(appliedZoneSetTempPath, customZoneJson);
|
JSONHelpers::TCustomZoneSetsMap customZoneSets;
|
||||||
|
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
|
||||||
|
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
|
||||||
|
|
||||||
//save different zone as deleted
|
//save different zone as deleted
|
||||||
json::JsonObject deletedCustomZoneSets = {};
|
json::JsonObject deletedCustomZoneSets = {};
|
||||||
@ -353,7 +364,7 @@ namespace FancyZonesUnitTests
|
|||||||
|
|
||||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||||
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
|
||||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
|
||||||
|
|
||||||
//temp file read on initialization
|
//temp file read on initialization
|
||||||
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
|
||||||
|
9
tools/FancyZonesEditor_DPI_netcore_test/App.xaml
Normal file
9
tools/FancyZonesEditor_DPI_netcore_test/App.xaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="FancyZonesEditor_DPI_netcore_test.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_netcore_test"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
17
tools/FancyZonesEditor_DPI_netcore_test/App.xaml.cs
Normal file
17
tools/FancyZonesEditor_DPI_netcore_test/App.xaml.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_netcore_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
10
tools/FancyZonesEditor_DPI_netcore_test/AssemblyInfo.cs
Normal file
10
tools/FancyZonesEditor_DPI_netcore_test/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.DpiAwareness" Version="6.7.30328" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30621.155
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZonesEditor_DPI_netcore_test", "FancyZonesEditor_DPI_netcore_test.csproj", "{1764AC88-EE10-4613-AB77-F213EF8043D6}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1764AC88-EE10-4613-AB77-F213EF8043D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1764AC88-EE10-4613-AB77-F213EF8043D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1764AC88-EE10-4613-AB77-F213EF8043D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1764AC88-EE10-4613-AB77-F213EF8043D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F95C5E3B-3EC1-42AC-A24C-6A9350B72807}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
15
tools/FancyZonesEditor_DPI_netcore_test/MainWindow.xaml
Normal file
15
tools/FancyZonesEditor_DPI_netcore_test/MainWindow.xaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<Window x:Class="FancyZonesEditor_DPI_netcore_test.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_netcore_test"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800"
|
||||||
|
Closing="Window_Closing">
|
||||||
|
<Grid>
|
||||||
|
<ListView x:Name="MonitorList" Margin="16">
|
||||||
|
|
||||||
|
</ListView>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
120
tools/FancyZonesEditor_DPI_netcore_test/MainWindow.xaml.cs
Normal file
120
tools/FancyZonesEditor_DPI_netcore_test/MainWindow.xaml.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Microsoft.VisualStudio.Utilities;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_netcore_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
struct ScreenInfo
|
||||||
|
{
|
||||||
|
public int MonitorDPI { get; set; }
|
||||||
|
|
||||||
|
public double WindowDPI { get; set; }
|
||||||
|
|
||||||
|
public Rect Resolution { get; set; }
|
||||||
|
|
||||||
|
public Rect WorkArea { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var resolution = String.Format("X:{0,5}, Y:{1,5}, W:{2,5}, H:{3,5}", Resolution.Left, Resolution.Top, Resolution.Width, Resolution.Height);
|
||||||
|
var workArea = String.Format("X:{0,5}, Y:{1,5}, W:{2,5}, H:{3,5}", WorkArea.Left, WorkArea.Top, WorkArea.Width, WorkArea.Height);
|
||||||
|
|
||||||
|
return "Monitor DPI: " + MonitorDPI + " - Resolution: " + resolution + " - WorkArea: " + workArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OverlayWindow> workAreaWindows;
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
double primaryMonitorDPI = 96f;
|
||||||
|
|
||||||
|
var colors = new Brush[] { Brushes.Green, Brushes.Blue, Brushes.Red };
|
||||||
|
|
||||||
|
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||||
|
List<ScreenInfo> screenInfoList = new List<ScreenInfo>();
|
||||||
|
|
||||||
|
workAreaWindows = new List<OverlayWindow>();
|
||||||
|
|
||||||
|
var monitors = MonitorsInfo.GetMonitors();
|
||||||
|
|
||||||
|
for (int i = 0; i < screens.Length; i++)
|
||||||
|
{
|
||||||
|
if (screens[i].Primary)
|
||||||
|
{
|
||||||
|
double monitorDPI;
|
||||||
|
DpiAwareness.GetMonitorDpi(monitors[i].MonitorHandle, out monitorDPI, out monitorDPI);
|
||||||
|
primaryMonitorDPI = monitorDPI;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < screens.Length; i++)
|
||||||
|
{
|
||||||
|
var monitor = monitors[i];
|
||||||
|
ScreenInfo screenInfo = new ScreenInfo();
|
||||||
|
var window = new OverlayWindow
|
||||||
|
{
|
||||||
|
Opacity = 0.8,
|
||||||
|
Background = colors[i % colors.Length],
|
||||||
|
BorderBrush = Brushes.White,
|
||||||
|
BorderThickness = new Thickness(4, 4, 4, 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
// get monitor dpi
|
||||||
|
double monitorDPI;
|
||||||
|
DpiAwareness.GetMonitorDpi(monitors[i].MonitorHandle, out monitorDPI, out monitorDPI);
|
||||||
|
screenInfo.MonitorDPI = (int)monitorDPI;
|
||||||
|
|
||||||
|
// resolution
|
||||||
|
screenInfo.Resolution = new Rect(monitor.MonitorInfo.monitor.left, monitor.MonitorInfo.monitor.top,
|
||||||
|
monitor.MonitorInfo.monitor.width, monitor.MonitorInfo.monitor.height);
|
||||||
|
|
||||||
|
// work area
|
||||||
|
Rect workedArea = new Rect(monitor.MonitorInfo.work.left, monitor.MonitorInfo.work.top,
|
||||||
|
monitor.MonitorInfo.work.width, monitor.MonitorInfo.work.height);
|
||||||
|
|
||||||
|
double scalePosition = 96f / primaryMonitorDPI;
|
||||||
|
workedArea.X *= scalePosition;
|
||||||
|
workedArea.Y *= scalePosition;
|
||||||
|
|
||||||
|
double scaleSize = 96f / monitorDPI;
|
||||||
|
workedArea.Width *= scaleSize;
|
||||||
|
workedArea.Height *= scaleSize;
|
||||||
|
|
||||||
|
screenInfo.WorkArea = workedArea;
|
||||||
|
|
||||||
|
screenInfo.WindowDPI = window.GetDpiX();
|
||||||
|
screenInfoList.Add(screenInfo);
|
||||||
|
|
||||||
|
// open window
|
||||||
|
window.Left = workedArea.X;
|
||||||
|
window.Top = workedArea.Y;
|
||||||
|
window.Width = workedArea.Width;
|
||||||
|
window.Height = workedArea.Height;
|
||||||
|
|
||||||
|
workAreaWindows.Add(window);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorList.ItemsSource = screenInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (OverlayWindow window in workAreaWindows)
|
||||||
|
{
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
156
tools/FancyZonesEditor_DPI_netcore_test/MonitorsInfo.cs
Normal file
156
tools/FancyZonesEditor_DPI_netcore_test/MonitorsInfo.cs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_netcore_test
|
||||||
|
{
|
||||||
|
public class MonitorsInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rectangle
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RECT
|
||||||
|
{
|
||||||
|
public int left;
|
||||||
|
public int top;
|
||||||
|
public int right;
|
||||||
|
public int bottom;
|
||||||
|
|
||||||
|
public int width
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return right - left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int height
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return bottom - top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct MONITORINFO
|
||||||
|
{
|
||||||
|
public uint size;
|
||||||
|
public RECT monitor;
|
||||||
|
public RECT work;
|
||||||
|
public uint flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor Enum Delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hMonitor">A handle to the display monitor.</param>
|
||||||
|
/// <param name="hdcMonitor">A handle to a device context.</param>
|
||||||
|
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor,
|
||||||
|
ref RECT lprcMonitor, IntPtr dwData);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates through the display monitors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hdc">A handle to a display device context that defines the visible region of interest.</param>
|
||||||
|
/// <param name="lprcClip">A pointer to a RECT structure that specifies a clipping rectangle.</param>
|
||||||
|
/// <param name="lpfnEnum">A pointer to a MonitorEnumProc application-defined callback function.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the MonitorEnumProc function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
|
||||||
|
MonitorEnumDelegate lpfnEnum, IntPtr dwData);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hmon">A handle to the display monitor of interest.</param>
|
||||||
|
/// <param name="mi">A pointer to a MONITORINFO instance created by this method.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool GetMonitorInfo(IntPtr hmon, ref MONITORINFO mi);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information with handle interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMonitorInfoWithHandle
|
||||||
|
{
|
||||||
|
IntPtr MonitorHandle { get; }
|
||||||
|
MONITORINFO MonitorInfo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information with handle.
|
||||||
|
/// </summary>
|
||||||
|
public class MonitorInfoWithHandle : IMonitorInfoWithHandle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The monitor handle.
|
||||||
|
/// </value>
|
||||||
|
public IntPtr MonitorHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor information.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The monitor information.
|
||||||
|
/// </value>
|
||||||
|
public MONITORINFO MonitorInfo { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MonitorInfoWithHandle"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="monitorHandle">The monitor handle.</param>
|
||||||
|
/// <param name="monitorInfo">The monitor information.</param>
|
||||||
|
public MonitorInfoWithHandle(IntPtr monitorHandle, MONITORINFO monitorInfo)
|
||||||
|
{
|
||||||
|
MonitorHandle = monitorHandle;
|
||||||
|
MonitorInfo = monitorInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor Enum Delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hMonitor">A handle to the display monitor.</param>
|
||||||
|
/// <param name="hdcMonitor">A handle to a device context.</param>
|
||||||
|
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
|
||||||
|
{
|
||||||
|
var mi = new MONITORINFO();
|
||||||
|
mi.size = (uint)Marshal.SizeOf(mi);
|
||||||
|
GetMonitorInfo(hMonitor, ref mi);
|
||||||
|
|
||||||
|
// Add to monitor info
|
||||||
|
_monitorInfos.Add(new MonitorInfoWithHandle(hMonitor, mi));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitors.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MonitorInfoWithHandle[] GetMonitors()
|
||||||
|
{
|
||||||
|
_monitorInfos = new List<MonitorInfoWithHandle>();
|
||||||
|
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
|
||||||
|
return _monitorInfos.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<MonitorInfoWithHandle> _monitorInfos;
|
||||||
|
}
|
||||||
|
}
|
14
tools/FancyZonesEditor_DPI_netcore_test/OverlayWindow.xaml
Normal file
14
tools/FancyZonesEditor_DPI_netcore_test/OverlayWindow.xaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Window x:Class="FancyZonesEditor_DPI_netcore_test.OverlayWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_netcore_test"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="OverlayWindow" Height="450" Width="800"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStyle="None"
|
||||||
|
AllowsTransparency="True"
|
||||||
|
Background="Transparent">
|
||||||
|
</Window>
|
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_netcore_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for OverlayWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class OverlayWindow : Window
|
||||||
|
{
|
||||||
|
public OverlayWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
tools/FancyZonesEditor_DPI_netcore_test/app.manifest
Normal file
74
tools/FancyZonesEditor_DPI_netcore_test/app.manifest
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC Manifest Options
|
||||||
|
If you want to change the Windows User Account Control level replace the
|
||||||
|
requestedExecutionLevel node with one of the following.
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||||
|
Remove this element if your application requires this virtualization for backwards
|
||||||
|
compatibility.
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||||
|
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||||
|
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||||
|
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30621.155
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZonesEditor_DPI_test", "FancyZonesEditor_DPI_test\FancyZonesEditor_DPI_test.csproj", "{B1708F5E-78F8-4646-86B5-0E83E3C79860}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B1708F5E-78F8-4646-86B5-0E83E3C79860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B1708F5E-78F8-4646-86B5-0E83E3C79860}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B1708F5E-78F8-4646-86B5-0E83E3C79860}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B1708F5E-78F8-4646-86B5-0E83E3C79860}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {B6C181B9-2BFF-4057-89D2-9BFDEBCC87F6}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||||
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="FancyZonesEditor_DPI_test.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_test"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{B1708F5E-78F8-4646-86B5-0E83E3C79860}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>FancyZonesEditor_DPI_test</RootNamespace>
|
||||||
|
<AssemblyName>FancyZonesEditor_DPI_test</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<PublishUrl>publish\</PublishUrl>
|
||||||
|
<Install>true</Install>
|
||||||
|
<InstallFrom>Disk</InstallFrom>
|
||||||
|
<UpdateEnabled>false</UpdateEnabled>
|
||||||
|
<UpdateMode>Foreground</UpdateMode>
|
||||||
|
<UpdateInterval>7</UpdateInterval>
|
||||||
|
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||||
|
<UpdatePeriodically>false</UpdatePeriodically>
|
||||||
|
<UpdateRequired>false</UpdateRequired>
|
||||||
|
<MapFileExtensions>true</MapFileExtensions>
|
||||||
|
<ApplicationRevision>0</ApplicationRevision>
|
||||||
|
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||||
|
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||||
|
<UseApplicationTrust>false</UseApplicationTrust>
|
||||||
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualStudio.DpiAwareness, Version=6.7.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.VisualStudio.DpiAwareness.6.7.30328\lib\net46\Microsoft.VisualStudio.DpiAwareness.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xaml">
|
||||||
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="App.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
<Page Include="MainWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="App.xaml.cs">
|
||||||
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainWindow.xaml.cs">
|
||||||
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Page Include="OverlayWindow.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="MonitorInfo.cs" />
|
||||||
|
<Compile Include="OverlayWindow.xaml.cs">
|
||||||
|
<DependentUpon>OverlayWindow.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="app.manifest" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
|
||||||
|
<Install>true</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||||
|
<Install>false</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
@ -0,0 +1,15 @@
|
|||||||
|
<Window x:Class="FancyZonesEditor_DPI_test.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_test"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800"
|
||||||
|
Closing="Window_Closing">
|
||||||
|
<Grid>
|
||||||
|
<ListView x:Name="MonitorList" Margin="16">
|
||||||
|
|
||||||
|
</ListView>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Microsoft.VisualStudio.Utilities;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
struct ScreenInfo
|
||||||
|
{
|
||||||
|
public int MonitorDPI { get; set; }
|
||||||
|
|
||||||
|
public double WindowDPI { get; set; }
|
||||||
|
|
||||||
|
public Rect Resolution { get; set; }
|
||||||
|
|
||||||
|
public Rect WorkArea { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var resolution = String.Format("X:{0,5}, Y:{1,5}, W:{2,5}, H:{3,5}", Resolution.Left, Resolution.Top, Resolution.Width, Resolution.Height);
|
||||||
|
var workArea = String.Format("X:{0,5}, Y:{1,5}, W:{2,5}, H:{3,5}", WorkArea.Left, WorkArea.Top, WorkArea.Width, WorkArea.Height);
|
||||||
|
|
||||||
|
return "Monitor DPI: " + MonitorDPI + " - Resolution: " + resolution + " - WorkArea: " + workArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OverlayWindow> workAreaWindows;
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
double primaryMonitorDPI = 96f;
|
||||||
|
|
||||||
|
var colors = new Brush[] { Brushes.Green, Brushes.Blue, Brushes.Red };
|
||||||
|
|
||||||
|
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||||
|
List<ScreenInfo> screenInfoList = new List<ScreenInfo>();
|
||||||
|
|
||||||
|
workAreaWindows = new List<OverlayWindow>();
|
||||||
|
|
||||||
|
var monitors = MonitorsInfo.GetMonitors();
|
||||||
|
|
||||||
|
for (int i = 0; i < screens.Length; i++)
|
||||||
|
{
|
||||||
|
if (screens[i].Primary)
|
||||||
|
{
|
||||||
|
double monitorDPI;
|
||||||
|
DpiAwareness.GetMonitorDpi(monitors[i].MonitorHandle, out monitorDPI, out monitorDPI);
|
||||||
|
primaryMonitorDPI = monitorDPI;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < screens.Length; i++)
|
||||||
|
{
|
||||||
|
var monitor = monitors[i];
|
||||||
|
ScreenInfo screenInfo = new ScreenInfo();
|
||||||
|
var window = new OverlayWindow
|
||||||
|
{
|
||||||
|
Opacity = 0.8,
|
||||||
|
Background = colors[i % colors.Length],
|
||||||
|
BorderBrush = Brushes.White,
|
||||||
|
BorderThickness = new Thickness(4, 4, 4, 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
// get monitor dpi
|
||||||
|
double monitorDPI;
|
||||||
|
DpiAwareness.GetMonitorDpi(monitors[i].MonitorHandle, out monitorDPI, out monitorDPI);
|
||||||
|
screenInfo.MonitorDPI = (int)monitorDPI;
|
||||||
|
|
||||||
|
// screen resolution
|
||||||
|
screenInfo.Resolution = new Rect(monitor.MonitorInfo.monitor.left, monitor.MonitorInfo.monitor.top,
|
||||||
|
monitor.MonitorInfo.monitor.width, monitor.MonitorInfo.monitor.height);
|
||||||
|
|
||||||
|
// work area
|
||||||
|
Rect workedArea = new Rect(monitor.MonitorInfo.work.left, monitor.MonitorInfo.work.top,
|
||||||
|
monitor.MonitorInfo.work.width, monitor.MonitorInfo.work.height);
|
||||||
|
|
||||||
|
double scaleFactor = 96f / primaryMonitorDPI;
|
||||||
|
workedArea.X *= scaleFactor;
|
||||||
|
workedArea.Y *= scaleFactor;
|
||||||
|
workedArea.Width *= scaleFactor;
|
||||||
|
workedArea.Height *= scaleFactor;
|
||||||
|
|
||||||
|
screenInfo.WorkArea = workedArea;
|
||||||
|
|
||||||
|
screenInfo.WindowDPI = window.GetDpiX();
|
||||||
|
screenInfoList.Add(screenInfo);
|
||||||
|
|
||||||
|
// open window
|
||||||
|
window.Left = workedArea.X;
|
||||||
|
window.Top = workedArea.Y;
|
||||||
|
window.Width = workedArea.Width;
|
||||||
|
window.Height = workedArea.Height;
|
||||||
|
|
||||||
|
workAreaWindows.Add(window);
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorList.ItemsSource = screenInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (OverlayWindow window in workAreaWindows)
|
||||||
|
{
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test
|
||||||
|
{
|
||||||
|
class MonitorsInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rectangle
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RECT
|
||||||
|
{
|
||||||
|
public int left;
|
||||||
|
public int top;
|
||||||
|
public int right;
|
||||||
|
public int bottom;
|
||||||
|
|
||||||
|
public int width
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return right - left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int height
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return bottom - top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct MONITORINFO
|
||||||
|
{
|
||||||
|
public uint size;
|
||||||
|
public RECT monitor;
|
||||||
|
public RECT work;
|
||||||
|
public uint flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor Enum Delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hMonitor">A handle to the display monitor.</param>
|
||||||
|
/// <param name="hdcMonitor">A handle to a device context.</param>
|
||||||
|
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor,
|
||||||
|
ref RECT lprcMonitor, IntPtr dwData);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates through the display monitors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hdc">A handle to a display device context that defines the visible region of interest.</param>
|
||||||
|
/// <param name="lprcClip">A pointer to a RECT structure that specifies a clipping rectangle.</param>
|
||||||
|
/// <param name="lpfnEnum">A pointer to a MonitorEnumProc application-defined callback function.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the MonitorEnumProc function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
|
||||||
|
MonitorEnumDelegate lpfnEnum, IntPtr dwData);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hmon">A handle to the display monitor of interest.</param>
|
||||||
|
/// <param name="mi">A pointer to a MONITORINFO instance created by this method.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool GetMonitorInfo(IntPtr hmon, ref MONITORINFO mi);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information with handle interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMonitorInfoWithHandle
|
||||||
|
{
|
||||||
|
IntPtr MonitorHandle { get; }
|
||||||
|
MONITORINFO MonitorInfo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor information with handle.
|
||||||
|
/// </summary>
|
||||||
|
public class MonitorInfoWithHandle : IMonitorInfoWithHandle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The monitor handle.
|
||||||
|
/// </value>
|
||||||
|
public IntPtr MonitorHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitor information.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The monitor information.
|
||||||
|
/// </value>
|
||||||
|
public MONITORINFO MonitorInfo { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MonitorInfoWithHandle"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="monitorHandle">The monitor handle.</param>
|
||||||
|
/// <param name="monitorInfo">The monitor information.</param>
|
||||||
|
public MonitorInfoWithHandle(IntPtr monitorHandle, MONITORINFO monitorInfo)
|
||||||
|
{
|
||||||
|
MonitorHandle = monitorHandle;
|
||||||
|
MonitorInfo = monitorInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Monitor Enum Delegate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hMonitor">A handle to the display monitor.</param>
|
||||||
|
/// <param name="hdcMonitor">A handle to a device context.</param>
|
||||||
|
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
|
||||||
|
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
|
||||||
|
{
|
||||||
|
var mi = new MONITORINFO();
|
||||||
|
mi.size = (uint)Marshal.SizeOf(mi);
|
||||||
|
GetMonitorInfo(hMonitor, ref mi);
|
||||||
|
|
||||||
|
// Add to monitor info
|
||||||
|
_monitorInfos.Add(new MonitorInfoWithHandle(hMonitor, mi));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the monitors.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static MonitorInfoWithHandle[] GetMonitors()
|
||||||
|
{
|
||||||
|
_monitorInfos = new List<MonitorInfoWithHandle>();
|
||||||
|
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
|
||||||
|
return _monitorInfos.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<MonitorInfoWithHandle> _monitorInfos;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<Window x:Class="FancyZonesEditor_DPI_test.OverlayWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:FancyZonesEditor_DPI_test"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="OverlayWindow" Height="450" Width="800"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStyle="None"
|
||||||
|
AllowsTransparency="True"
|
||||||
|
Background="Transparent"
|
||||||
|
DpiChanged="Window_DpiChanged"/>
|
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for OverlayWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class OverlayWindow : Window
|
||||||
|
{
|
||||||
|
public OverlayWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_DpiChanged(object sender, DpiChangedEventArgs e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("FancyZonesEditor_DPI_test")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("FancyZonesEditor_DPI_test")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
//In order to begin building localizable applications, set
|
||||||
|
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||||
|
//inside a <PropertyGroup>. For example, if you are using US english
|
||||||
|
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||||
|
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||||
|
//the line below to match the UICulture setting in the project file.
|
||||||
|
|
||||||
|
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
71
tools/FancyZonesEditor_DPI_test/FancyZonesEditor_DPI_test/Properties/Resources.Designer.cs
generated
Normal file
71
tools/FancyZonesEditor_DPI_test/FancyZonesEditor_DPI_test/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FancyZonesEditor_DPI_test.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
30
tools/FancyZonesEditor_DPI_test/FancyZonesEditor_DPI_test/Properties/Settings.Designer.cs
generated
Normal file
30
tools/FancyZonesEditor_DPI_test/FancyZonesEditor_DPI_test/Properties/Settings.Designer.cs
generated
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace FancyZonesEditor_DPI_test.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC Manifest Options
|
||||||
|
If you want to change the Windows User Account Control level replace the
|
||||||
|
requestedExecutionLevel node with one of the following.
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||||
|
Remove this element if your application requires this virtualization for backwards
|
||||||
|
compatibility.
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||||
|
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||||
|
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||||
|
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">System</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.VisualStudio.DpiAwareness" version="6.7.30328" targetFramework="net472" />
|
||||||
|
</packages>
|
Loading…
Reference in New Issue
Block a user