[FancyZones Editor] New UX for the FZ editor. (#9325)

* Removed MetroWindow, added theming support and modernWPF

* Rmoved MahApps refs

* Removed MahApps

* Updated canvas zones

* Updated GridEditor

* Fixes

* UI updates

* New layout type selection dialog

* New editor UI

* Updates

* Fix

* UI enhancements

* Updated UI

* Added styles to layoutpreview

* Accesibility improvements

* Accesibility and styling improvements

* Fix

* Cleaned up brushes

* Updated UX

* Updated UI

* Added no layouts description

* Fix

* UI fixes

* [FZ Editor] Serialize/deserialize settings (#8615)

* conflicts fix

* [FZ Editor] Parse json file instead of command line args (#8649)

* [FZ Editor] Serialize/deserialize settings fix (#8707)

* [FZ Editor] Hide unsupported settings in custom layouts flyouts (#8716)

* [FZ Editor] Duplicate custom layouts (#8718)

* [FZ Editor] Duplicate layout behavior (#8720)

* New UX proposal

* Updated spacing

* Switching to toggleswitches

* Revert toggleswitch

* Updated colorbrush

* Updated string for saving label

* Updated UI

* Dark theme color fixes

* Removed space

* [FZ Editor] Bind dialog properties (#9199)

* Resize editor window to fit the content in single-monitor mode (#9203)

* Editor opening fix (#9207)

* Disable "Create" button if the Name textbox is empty (#9212)

* [FZ Editor] Changed edit dialog for template layouts. (#9233)

* [FZ Editor] Small fixes and refactoring. (#9236)

* new layout creation refactoring
* "Save and apply" applies the layout
* number of zones header hide

* [FZ Editor] Empty layout template. (#9237)

* [FZ Editor] Move "Duplicate" and "Delete" buttons to the Edit dialog. (#9272)

* [FZ Editor] Preview the applied layout after editing another layout. (#9278)

* Fixed "Save and apply" button behavior (#9286)

* [FZ Editor] Save template layouts in the settings. (#9283)

* Added default custom layout name (#9291)

* close dialog before opening zones editor (#9302)

* Pressing Esc closes dialogs (#9301)

* [FZ Editor] Reset applied layout to "No layout" if it was deleted. (#9315)

* [FZ Editor] Dark theme colors (#9317)

* "Number of zones" buttons colors. (#9321)

* rebase fix

* added ModernWpf.dll

* address PR comments: updated colors

* added comments, replaced magic numbers

* refactoring

* merge zones crash fix

* removed redundant using directive

Co-authored-by: Niels Laute <niels9001@hotmail.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Seraphima Zykova 2021-01-27 21:33:52 +03:00 committed by GitHub
parent eb15cdde1b
commit 646d61bd4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 3664 additions and 2781 deletions

View file

@ -2374,6 +2374,7 @@ Whichdoes
whitespaces
WIC
Wifi
wifstream
wih
wiki
wikipedia

View file

@ -374,8 +374,9 @@
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\FancyZonesEditor.runtimeconfig.json" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\FancyZonesEditor.exe" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\ControlzEx.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\MahApps.Metro.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\Microsoft.Xaml.Behaviors.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\ModernWpf.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\ModernWpf.Controls.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\System.Text.Json.dll" />
<File Id="FancyZones_ManagedCommon" Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\ManagedCommon.dll" />
<File Id="FancyZones_Telemetry.dll" Source="$(var.BinX64Dir)modules\$(var.FancyZonesProjectName)\ManagedTelemetry.dll" />

View file

@ -2,20 +2,29 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FancyZonesEditor"
xmlns:ui="http://schemas.modernwpf.com/2019"
Startup="OnStartup">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
<ui:ThemeResources />
<ui:XamlControlsResources />
<ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyles.xaml" />
<ResourceDictionary Source="pack://application:,,,/Styles/LayoutPreviewStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF333333"/>
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#FF1a1a1a"/>
<Style x:Key="UWPFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border Margin="-2"
CornerRadius="4"
BorderThickness="2"
BorderBrush="{DynamicResource PrimaryForegroundBrush}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View file

@ -9,6 +9,8 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using FancyZonesEditor.Utils;
using ManagedCommon;
@ -41,6 +43,9 @@ namespace FancyZonesEditor
private const string CrashReportDynamicAssemblyTag = "dynamic assembly doesn't have location";
private const string CrashReportLocationNullTag = "location is null or empty";
private const string ParsingErrorReportTag = "Settings parsing error";
private const string ParsingErrorDataTag = "Data: ";
public MainWindowSettingsModel MainWindowSettings { get; }
public static FancyZonesEditorIO FancyZonesEditorIO { get; private set; }
@ -49,6 +54,8 @@ namespace FancyZonesEditor
public static int PowerToysPID { get; set; }
private ThemeManager _themeManager;
public static bool DebugMode
{
get
@ -67,7 +74,7 @@ namespace FancyZonesEditor
public App()
{
DebugModeCheck();
// DebugModeCheck();
FancyZonesEditorIO = new FancyZonesEditorIO();
Overlay = new Overlay();
MainWindowSettings = new MainWindowSettingsModel();
@ -82,8 +89,61 @@ namespace FancyZonesEditor
Environment.Exit(0);
});
FancyZonesEditorIO.ParseCommandLineArguments();
FancyZonesEditorIO.ParseDeviceInfoData();
_themeManager = new ThemeManager(this);
if (!FancyZonesEditorIO.ParseParams().Result)
{
FancyZonesEditorIO.ParseCommandLineArguments();
}
var parseResult = FancyZonesEditorIO.ParseZoneSettings();
// 10ms retry loop with 1 second timeout
if (!parseResult.Result)
{
CancellationTokenSource ts = new CancellationTokenSource();
Task t = Task.Run(() =>
{
while (!parseResult.Result && !ts.IsCancellationRequested)
{
Task.Delay(10).Wait();
parseResult = FancyZonesEditorIO.ParseZoneSettings();
}
});
try
{
bool result = t.Wait(1000, ts.Token);
ts.Cancel();
}
catch (OperationCanceledException)
{
ts.Dispose();
}
}
// Error message if parsing failed
if (!parseResult.Result)
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("## " + ParsingErrorReportTag);
sb.AppendLine();
sb.AppendLine(parseResult.Message);
sb.AppendLine();
sb.AppendLine(ParsingErrorDataTag);
sb.AppendLine(parseResult.MalformedData);
string message = parseResult.Message + Environment.NewLine + Environment.NewLine + FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_User_Choice;
if (MessageBox.Show(message, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Title, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
// TODO: log error
ShowExceptionReportMessageBox(sb.ToString());
Environment.Exit(0);
}
ShowExceptionReportMessageBox(sb.ToString());
}
MainWindowSettingsModel settings = ((App)Current).MainWindowSettings;
settings.UpdateSelectedLayoutModel();

View file

@ -1,202 +1,69 @@
<local:EditorWindow x:Class="FancyZonesEditor.CanvasEditorWindow"
AutomationProperties.Name="{x:Static props:Resources.Canvas_Layout_Editor}"
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:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:FancyZonesEditor"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
mc:Ignorable="d"
Title=""
Width="528"
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Closed="OnClosed">
AutomationProperties.Name="{x:Static props:Resources.Canvas_Layout_Editor}"
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"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
mc:Ignorable="d"
Title=""
Width="320"
BorderThickness="0"
xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.IsIconVisible="True"
SizeToContent="Height"
Background="{DynamicResource PrimaryBackgroundBrush}"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Closed="OnClosed">
<Grid>
<Grid Height="36"
Background="{DynamicResource SecondaryBackgroundBrush}"
Margin="0,-36,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Stretch">
<Border Background="{DynamicResource TitleBarSecondaryForegroundBrush}"
Width="30"
Height="3"
CornerRadius="1.5"
VerticalAlignment="Center"
Margin="0,4,0,0" />
</Grid>
<StackPanel Margin="16"
FocusManager.FocusedElement="{Binding ElementName=newZoneButton}">
<Button x:Name="newZoneButton"
AutomationProperties.LabeledBy="{Binding ElementName=newZoneName}"
HorizontalAlignment="Stretch"
Height="64"
Width="64"
ui:ControlHelper.CornerRadius="64"
Margin="0,8,0,0"
Style="{StaticResource AccentButtonStyle}"
FontFamily="Segoe MDL2 Assets"
Content="&#xE109;"
FontSize="24"
ToolTip="{x:Static props:Resources.Add_zone}"
Click="OnAddZone" />
<Window.Resources>
<Style x:Key="titleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LineHeight" Value="24" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="16,16,0,12" />
</Style>
<Style x:Key="zoneCount" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="FontSize" Value="24"/>
<Setter Property="LineHeight" Value="24" />
<Setter Property="Margin" Value="20,0,20,0" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="tabText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="#C4C4C4"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="24,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<Style x:Key="settingText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="5,10,0,0" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="templateTitleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="250"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style x:Key="customButtonFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="1" Stroke="White" StrokeDashArray="1 2" StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="secondaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#767676"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#5D5D5D"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="primaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#0078D7"/>
<Setter Property="Margin" Value="16,10,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#024D89"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="spinnerButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="0,0,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="0,0,0,0" />
</Style>
<Style x:Key="templateBackground" TargetType="Rectangle">
<Setter Property="Fill" Value="Black"/>
<Setter Property="Opacity" Value="0.05"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
</Style>
<Style x:Key="templateBackgroundSelected" TargetType="Rectangle">
<Setter Property="Fill" Value="#F2F2F2"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
<Setter Property="Stroke" Value="#0078D7"/>
<Setter Property="StrokeThickness" Value="2"/>
</Style>
<Style x:Key="newZoneButton" TargetType="Button">
<Setter Property="Background" Value="#f2f2f2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="120"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="textLabel" TargetType="TextBlock">
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="16,12,0,0"/>
</Style>
<Style x:Key="textBox" TargetType="TextBox">
<Setter Property="BorderBrush" Value="#949494"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,5,0,0"/>
<Setter Property="Padding" Value="5,5,5,5"/>
</Style>
</Window.Resources>
<StackPanel FocusManager.FocusedElement="{Binding ElementName=newZoneButton}">
<TextBlock Name="windowEditorDialogTitle" Text="{x:Static props:Resources.Custom_Layout_Creator}" Style="{StaticResource titleText}" />
<Button x:Name="newZoneButton" AutomationProperties.LabeledBy="{Binding ElementName=newZoneName}" Width="496" Height="136" Style="{StaticResource newZoneButton}" Click="OnAddZone">
<StackPanel>
<TextBlock x:Name="newZoneName" Text="{x:Static props:Resources.Add_zone}" Style="{StaticResource templateTitleText}" />
<TextBlock HorizontalAlignment="Center" Text="+" Margin="0,-40,0,0"/>
</StackPanel>
</Button>
<TextBlock x:Name="customLayoutName" Text="{x:Static props:Resources.Name}" Style="{StaticResource textLabel}" />
<TextBox Text="{Binding Name}" AutomationProperties.LabeledBy="{Binding ElementName=customLayoutName}" Width="496" Style="{StaticResource textBox}" />
<!--
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="16,4,0,0"/>
<TextBlock Text="Show snap grid" Style="{StaticResource settingText}" />
<TextBlock Text="Grid spacing" Style="{StaticResource settingText}" Margin="40,10,10,0" />
<TextBox x:Name="gridValue" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}"/>
<Grid Margin="0,24,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="{x:Static props:Resources.Cancel}"
Style="{StaticResource DefaultButtonStyle}"
HorizontalAlignment="Stretch"
Grid.Column="2"
Click="OnCancel" />
<Button Content="{x:Static props:Resources.Save_Apply}"
Style="{StaticResource AccentButtonStyle}"
HorizontalAlignment="Stretch"
Click="OnSaveApplyTemplate" />
</Grid>
</StackPanel>
-->
<StackPanel Orientation="Horizontal" Margin="0,12,0,16">
<Button Content="{x:Static props:Resources.Cancel}" Style="{StaticResource secondaryButton}" Click="OnCancel"/>
<Button Content="{x:Static props:Resources.Save_Apply}" Style="{StaticResource primaryButton}" Click="OnSaveApplyTemplate" />
</StackPanel>
</StackPanel>
</local:EditorWindow>
</Grid>
</local:EditorWindow>

View file

@ -8,11 +8,23 @@ using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for windowEditor.xaml
/// </summary>
public partial class CanvasEditorWindow : EditorWindow
{
// Default distance from the top and left borders to the zone.
private const int DefaultOffset = 100;
// Next created zone will be by OffsetShift value below and to the right of the previous.
private const int OffsetShift = 50;
// Zone size depends on the work area size multiplied by ZoneSizeMultiplier value.
private const double ZoneSizeMultiplier = 0.4;
// Distance from the top and left borders to the zone.
private int _offset = DefaultOffset;
private CanvasLayoutModel _model;
private CanvasLayoutModel _stashedModel;
public CanvasEditorWindow()
{
InitializeComponent();
@ -28,19 +40,19 @@ namespace FancyZonesEditor
Rect workingArea = App.Overlay.WorkArea;
int offset = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(_offset);
if (offset + (int)(workingArea.Width * 0.4) < (int)workingArea.Width
&& offset + (int)(workingArea.Height * 0.4) < (int)workingArea.Height)
if (offset + (int)(workingArea.Width * ZoneSizeMultiplier) < (int)workingArea.Width
&& offset + (int)(workingArea.Height * ZoneSizeMultiplier) < (int)workingArea.Height)
{
_model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * 0.4), (int)(workingArea.Height * 0.4)));
_model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * ZoneSizeMultiplier), (int)(workingArea.Height * ZoneSizeMultiplier)));
}
else
{
_offset = 100;
_offset = DefaultOffset;
offset = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(_offset);
_model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * 0.4), (int)(workingArea.Height * 0.4)));
_model.AddZone(new Int32Rect(offset, offset, (int)(workingArea.Width * ZoneSizeMultiplier), (int)(workingArea.Height * ZoneSizeMultiplier)));
}
_offset += 50; // TODO: replace hardcoded numbers
_offset += OffsetShift;
}
protected new void OnCancel(object sender, RoutedEventArgs e)
@ -56,9 +68,5 @@ namespace FancyZonesEditor
OnCancel(sender, null);
}
}
private int _offset = 100;
private CanvasLayoutModel _model;
private CanvasLayoutModel _stashedModel;
}
}

View file

@ -2,7 +2,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Background="Transparent"
@ -15,7 +16,7 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" >
<VisualStateGroup.Transitions>
@ -43,16 +44,27 @@
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryForegroundBrush}" />
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Border x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter"
Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource SystemControlBackgroundAccentBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
@ -67,7 +79,10 @@
</Style>
</UserControl.Resources>
<Border BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" Background="{StaticResource CanvasZoneBackgroundBrush}" BorderThickness="1">
<Border BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
Background="{DynamicResource CanvasZoneBackgroundBrush}"
CornerRadius="4"
BorderThickness="1">
<Grid x:Name="Frame">
<Grid.RowDefinitions>
<RowDefinition Height="8"/>
@ -89,8 +104,8 @@
Canvas.Left="10"
Canvas.Bottom="10"
FontSize="64"
FontFamily="Segoe UI Light"
Foreground="White"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Grid.Column="2"
Grid.Row="2"
VerticalContentAlignment="Center"
@ -108,10 +123,20 @@
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Button Content="&#xE894;" BorderThickness="0" ToolTip="Delete zone" Background="Transparent" Foreground="White" FontSize="16" Padding="4" Click="OnClose" Grid.Row="2" Grid.Column="2" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
<Button Content="&#xE894;"
BorderThickness="0"
ToolTip="{x:Static props:Resources.Delete_Zone}"
Background="Transparent"
FontSize="16"
Padding="4"
Click="OnClose"
Grid.Row="2"
Grid.Column="2"
FontFamily="Segoe MDL2 Assets"
HorizontalAlignment="Right"
VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
<Canvas x:Name="Body" />
</Grid>
</Border>
</UserControl>

View file

@ -0,0 +1,25 @@
// 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.Globalization;
using System.Windows;
using System.Windows.Data;
using FancyZonesEditor.Models;
namespace FancyZonesEditor.Converters
{
public class LayoutModelTypeBlankToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (LayoutType)value == LayoutType.Blank ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}

View file

@ -3,24 +3,22 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using FancyZonesEditor.Models;
namespace FancyZonesEditor.Converters
{
public class BooleanToBrushConverter : IValueConverter
public class LayoutModelTypeToVisibilityConverter : IValueConverter
{
private static readonly Brush _selectedBrush = new SolidColorBrush(Color.FromRgb(0x00, 0x78, 0xD7));
private static readonly Brush _normalBrush = new SolidColorBrush(Color.FromRgb(0xF2, 0xF2, 0xF2));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? _selectedBrush : _normalBrush;
return value is CanvasLayoutModel ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == _selectedBrush;
return null;
}
}
}

View file

@ -0,0 +1,25 @@
// 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.Globalization;
using System.Windows;
using System.Windows.Data;
using FancyZonesEditor.Models;
namespace FancyZonesEditor.Converters
{
public class LayoutTypeCustomToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (LayoutType)value == LayoutType.Custom ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}

View file

@ -0,0 +1,25 @@
// 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.Globalization;
using System.Windows;
using System.Windows.Data;
using FancyZonesEditor.Models;
namespace FancyZonesEditor.Converters
{
public class LayoutTypeTemplateToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (LayoutType)value != LayoutType.Custom ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}

View file

@ -5,11 +5,10 @@
using System;
using System.Windows;
using FancyZonesEditor.Models;
using MahApps.Metro.Controls;
namespace FancyZonesEditor
{
public class EditorWindow : MetroWindow
public class EditorWindow : Window
{
protected void OnSaveApplyTemplate(object sender, RoutedEventArgs e)
{
@ -24,9 +23,13 @@ namespace FancyZonesEditor
}
model.Persist();
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
settings.SetAppliedModel(model);
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
}
LayoutModel.SerializeDeletedCustomZoneSets();
App.FancyZonesEditorIO.SerializeZoneSettings();
_backToLayoutPicker = false;
Close();

View file

@ -65,19 +65,14 @@
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MahApps.Metro" Version="2.3.2" />
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="ControlzEx" Version="4.4.0" />
<PackageReference Include="ModernWpfUI" Version="0.9.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Resource Include="images\ChromeClose.png" />
<Resource Include="images\Delete.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\Merge.png" />
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\FancyZonesEditor.ico" />
@ -86,6 +81,11 @@
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>

View file

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using FancyZonesEditor.Models;
using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@ -120,25 +119,29 @@ namespace FancyZonesEditor
{
_colInfo.Add(new RowColInfo(model.ColumnPercents[col]));
}
int maxIndex = 0;
for (int row = 0; row < _model.Rows; row++)
{
for (int col = 0; col < _model.Columns; col++)
{
maxIndex = Math.Max(maxIndex, _model.CellChildMap[row, col]);
}
}
_zoneCount = maxIndex + 1;
}
public int ZoneCount
{
get
{
int maxIndex = 0;
for (int row = 0; row < _model.Rows; row++)
{
for (int col = 0; col < _model.Columns; col++)
{
maxIndex = Math.Max(maxIndex, _model.CellChildMap[row, col]);
}
}
return maxIndex;
return _zoneCount;
}
}
private int _zoneCount;
public Tuple<int, int> RowColByIndex(int index)
{
int foundRow = -1;
@ -238,6 +241,7 @@ namespace FancyZonesEditor
FixAccuracyError(_colInfo, _model.ColumnPercents);
_model.CellChildMap = newCellChildMap;
_model.Columns++;
_model.UpdatePreview();
}
public void SplitRow(int foundRow, int spliteeIndex, int newChildIndex, double space, double offset, double actualHeight)
@ -289,6 +293,7 @@ namespace FancyZonesEditor
FixAccuracyError(_rowInfo, _model.RowPercents);
_model.CellChildMap = newCellChildMap;
_model.Rows++;
_model.UpdatePreview();
}
public void SplitOnDrag(GridResizer resizer, double delta, double space)
@ -447,6 +452,8 @@ namespace FancyZonesEditor
_model.CellChildMap = newCellChildMap;
_model.Rows++;
}
_model.UpdatePreview();
}
public void RecalculateZones(int spacing, Size arrangeSize)
@ -474,6 +481,11 @@ namespace FancyZonesEditor
public void ArrangeZones(UIElementCollection zones, int spacing)
{
if (zones.Count == 0)
{
return;
}
int rows = _model.Rows;
int cols = _model.Columns;
int[,] cells = _model.CellChildMap;
@ -1029,6 +1041,7 @@ namespace FancyZonesEditor
_model.Rows = rows;
_model.Columns = cols;
_model.UpdatePreview();
}
private void FixAccuracyError(List<RowColInfo> info, List<int> percents)

View file

@ -5,47 +5,40 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="#F2F2F2" />
<Setter Property="Width" Value="150" />
</Style>
</UserControl.Resources>
<Viewbox Stretch="Uniform">
<Grid>
<Canvas x:Name="Preview" />
<Canvas x:Name="AdornerLayer" />
<Canvas
x:Name="MergePanel"
MouseUp="MergePanelMouseUp"
Visibility="Collapsed">
<StackPanel
x:Name="MergeButtons"
Background="Gray"
Orientation="Horizontal">
<Button
Width="134"
Height="36"
Margin="0"
Click="MergeClick">
<StackPanel Orientation="Horizontal">
<Image
Height="16"
Margin="0,0,12,0"
HorizontalAlignment="Left"
Source="images/Merge.png" />
<TextBlock Text="Merge zones" />
</StackPanel>
</Button>
<Canvas x:Name="MergePanel"
MouseUp="MergePanelMouseUp"
Visibility="Collapsed">
<Canvas.Effect>
<DropShadowEffect BlurRadius="6" Opacity="0.32" ShadowDepth="2" />
</Canvas.Effect>
<StackPanel x:Name="MergeButtons"
Orientation="Horizontal">
<Border CornerRadius="2"
Background="{DynamicResource PrimaryBackgroundBrush}">
<Button Width="134"
Height="36"
Margin="0"
Style="{StaticResource DefaultButtonStyle}"
Click="MergeClick">
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE746;"
FontFamily="Segoe MDL2 Assets"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Margin="0,3,8,0" />
<TextBlock Text="{x:Static props:Resources.Merge_zones}"
Foreground="{DynamicResource PrimaryForegroundBrush}"/>
</StackPanel>
</Button>
</Border>
</StackPanel>
</Canvas>
</Grid>

View file

@ -8,7 +8,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using FancyZonesEditor.Models;
using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
@ -40,27 +39,27 @@ namespace FancyZonesEditor
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
{
GridLayoutModel model = (GridLayoutModel)DataContext;
if (model != null)
if (model == null)
{
_data = new GridData(model);
_dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted);
int zoneCount = _data.ZoneCount;
for (int i = 0; i <= zoneCount; i++)
{
AddZone();
}
return;
}
_data = new GridData(model);
_dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted);
_dragHandles.InitDragHandles(model);
Model = model;
if (Model == null)
Model.PropertyChanged += OnGridDimensionsChanged;
int zoneCount = _data.ZoneCount;
for (int i = 0; i < zoneCount; i++)
{
Model = new GridLayoutModel();
DataContext = Model;
AddZone();
}
Model.PropertyChanged += OnGridDimensionsChanged;
_dragHandles.InitDragHandles(model);
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
}
private void GridEditor_Unloaded(object sender, RoutedEventArgs e)
@ -330,15 +329,17 @@ namespace FancyZonesEditor
zone.Visibility = Visibility.Visible;
return freeIndex;
}
zone = new GridZone(Model.ShowSpacing ? Model.Spacing : 0);
zone.Split += OnSplit;
zone.MergeDrag += OnMergeDrag;
zone.MergeComplete += OnMergeComplete;
zone.FullSplit += OnFullSplit;
Preview.Children.Add(zone);
return Preview.Children.Count - 1;
}
zone = new GridZone();
zone.Split += OnSplit;
zone.MergeDrag += OnMergeDrag;
zone.MergeComplete += OnMergeComplete;
zone.FullSplit += OnFullSplit;
Preview.Children.Add(zone);
return Preview.Children.Count - 1;
return 0;
}
private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
@ -372,7 +373,7 @@ namespace FancyZonesEditor
Preview.Height = workArea.Height;
GridLayoutModel model = Model;
if (model == null)
if (model == null || _data == null)
{
return;
}
@ -383,9 +384,7 @@ namespace FancyZonesEditor
return;
}
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
int spacing = model.ShowSpacing ? model.Spacing : 0;
_data.RecalculateZones(spacing, arrangeSize);
_data.ArrangeZones(Preview.Children, spacing);
@ -411,10 +410,10 @@ namespace FancyZonesEditor
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
{
double spacing = 0;
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (settings.ShowSpacing)
GridLayoutModel model = Model;
if (model.ShowSpacing)
{
spacing = settings.Spacing;
spacing = model.Spacing;
}
_data.SplitOnDrag(resizer, delta, spacing);

View file

@ -1,196 +1,62 @@
<local:EditorWindow x:Class="FancyZonesEditor.GridEditorWindow"
AutomationProperties.Name="{x:Static props:Resources.Grid_Layout_Editor}"
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:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:FancyZonesEditor"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
mc:Ignorable="d"
Title=""
Width="528"
SizeToContent="Height"
Background="White"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Closed="OnClosed">
AutomationProperties.Name="{x:Static props:Resources.Grid_Layout_Editor}"
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"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
mc:Ignorable="d"
Title=""
Width="320"
BorderThickness="0"
xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.IsIconVisible="True"
SizeToContent="Height"
Background="{DynamicResource PrimaryBackgroundBrush}"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Closed="OnClosed">
<Grid>
<Grid
Height="36"
Background="{DynamicResource SecondaryBackgroundBrush}"
Margin="0,-36,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Stretch">
<Window.Resources>
<Style x:Key="titleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LineHeight" Value="24" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="0,0,0,12" />
</Style>
<Style x:Key="zoneCount" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="FontSize" Value="24"/>
<Setter Property="LineHeight" Value="24" />
<Setter Property="Margin" Value="20,0,20,0" />
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="tabText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="#C4C4C4"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="24,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<Style x:Key="settingText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="LineHeight" Value="20" />
<Setter Property="Margin" Value="5,10,0,0" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
<Style x:Key="templateTitleText" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Regular" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0,20,0,0" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="250"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style x:Key="customButtonFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="1" Stroke="White" StrokeDashArray="1 2" StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="secondaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#767676"/>
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#5D5D5D"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="primaryButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="0,5,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#0078D7"/>
<Setter Property="Margin" Value="16,0,0,0" />
<Setter Property="Width" Value="239"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FocusVisualStyle" Value="{DynamicResource customButtonFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#024D89"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="spinnerButton" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="0,0,0,5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="#F2F2F2"/>
<Setter Property="Margin" Value="0,0,0,0" />
</Style>
<Style x:Key="templateBackground" TargetType="Rectangle">
<Setter Property="Fill" Value="Black"/>
<Setter Property="Opacity" Value="0.05"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
</Style>
<Style x:Key="templateBackgroundSelected" TargetType="Rectangle">
<Setter Property="Fill" Value="#F2F2F2"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
<Setter Property="Stroke" Value="#0078D7"/>
<Setter Property="StrokeThickness" Value="2"/>
</Style>
<Style x:Key="newZoneButton" TargetType="Button">
<Setter Property="Background" Value="#f2f2f2"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="FontSize" Value="120"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="textLabel" TargetType="TextBlock">
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="0,12,0,0"/>
</Style>
<Style x:Key="textBox" TargetType="TextBox">
<Setter Property="BorderBrush" Value="#949494"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="SegoeUI"/>
<Setter Property="FontWeight" Value="Regular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="5,5,5,5"/>
</Style>
</Window.Resources>
<StackPanel Margin="16">
<TextBlock Name="windowEditorDialogTitle" Text="{x:Static props:Resources.Custom_Table_Layout}" Style="{StaticResource titleText}" />
<TextBlock Text="{x:Static props:Resources.Note_Custom_Table}" Style="{StaticResource textLabel}" TextWrapping="Wrap" />
<TextBlock x:Name="customLayoutName" Text="{x:Static props:Resources.Name}" Style="{StaticResource textLabel}" />
<TextBox Name="customLayoutNameTextBox" Text="{Binding Name}" GotFocus="NameTextBox_GotFocus" AutomationProperties.LabeledBy="{Binding ElementName=customLayoutName}" Style="{StaticResource textBox}" />
<!--
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="21,4,0,0"/>
<TextBlock Text="Show snap grid" Style="{StaticResource settingText}" />
<TextBlock Text="Grid spacing" Style="{StaticResource settingText}" Margin="40,10,10,0" />
<TextBox x:Name="gridValue" Text="{Binding Path=Spacing,Mode=TwoWay}" Style="{StaticResource textBox}"/>
<Border
Background="{DynamicResource TitleBarSecondaryForegroundBrush}"
Width="30"
Height="3"
CornerRadius="1.5"
VerticalAlignment="Center"
Margin="0,4,0,0" />
</Grid>
<StackPanel Margin="16">
<TextBlock Text="{x:Static props:Resources.Note_Custom_Table}"
Foreground="{DynamicResource SecondaryForegroundBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="0,8,0,0"
TextWrapping="Wrap" />
<Grid Margin="0,24,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Content="{x:Static props:Resources.Cancel}"
Style="{StaticResource DefaultButtonStyle}"
HorizontalAlignment="Stretch"
Grid.Column="2"
Click="OnCancel" />
<Button Content="{x:Static props:Resources.Save_Apply}"
Style="{StaticResource AccentButtonStyle}"
HorizontalAlignment="Stretch"
Click="OnSaveApplyTemplate" />
</Grid>
</StackPanel>
-->
<StackPanel Orientation="Horizontal" Margin="0,16,0,0">
<Button Content="{x:Static props:Resources.Cancel}" Style="{StaticResource secondaryButton}" Click="OnCancel" />
<Button Content="{x:Static props:Resources.Save_Apply}" Style="{StaticResource primaryButton}" Click="OnSaveApplyTemplate" />
</StackPanel>
</StackPanel>
</Grid>
</local:EditorWindow>

View file

@ -5,13 +5,9 @@
using System.Windows;
using System.Windows.Input;
using FancyZonesEditor.Models;
using FancyZonesEditor.Utils;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class GridEditorWindow : EditorWindow
{
public GridEditorWindow()
@ -42,15 +38,5 @@ namespace FancyZonesEditor
}
private GridLayoutModel _stashedModel;
private void NameTextBox_GotFocus(object sender, RoutedEventArgs e)
{
customLayoutNameTextBox.CaretIndex = customLayoutNameTextBox.Text.Length;
}
public System.Windows.Controls.TextBox NameTextBox()
{
return customLayoutNameTextBox;
}
}
}

View file

@ -8,15 +8,32 @@
d:DesignHeight="300" d:DesignWidth="300">
<Thumb.Template>
<ControlTemplate>
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
<Ellipse x:Name="BackgroundEllipse" Height="48" Width="48" Fill="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" />
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
</StackPanel>
<Border x:Name="Body"
Height="48"
Width="48"
CornerRadius="48"
Background="{DynamicResource SystemControlBackgroundAccentBrush}">
<Border.Effect>
<DropShadowEffect BlurRadius="6"
Opacity="0.24"
ShadowDepth="1" />
</Border.Effect>
<Grid>
<Rectangle Height="20"
Width="2"
Fill="White"
Margin="5,0,0,0" />
<Rectangle Height="20"
Width="2"
Fill="White"
Margin="-5,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="BackgroundEllipse" Value="0.6"/>
<Setter Property="Background"
TargetName="Body"
Value="{DynamicResource SystemAccentColorLight1Brush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

View file

@ -45,7 +45,7 @@ namespace FancyZonesEditor
{
_orientation = value;
ApplyTemplate();
StackPanel body = (StackPanel)Template.FindName("Body", this);
Border body = (Border)Template.FindName("Body", this);
if (value == Orientation.Vertical)
{
body.RenderTransform = null;

View file

@ -4,13 +4,14 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
Background="{StaticResource GridZoneBackgroundBrush}"
BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"
Background="{DynamicResource GridZoneBackgroundBrush}"
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
BorderThickness="1"
Opacity="0.8"
Opacity="1"
mc:Ignorable="d">
<Grid x:Name="Frame">
<Canvas x:Name="Body" />
@ -23,9 +24,9 @@
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="ID"
FontFamily="Segoe UI Light"
FontWeight="SemiBold"
FontSize="64"
Foreground="White" />
Foreground="{DynamicResource PrimaryForegroundBrush}" />
<!--<TextBlock Margin="2" Text="Shift Key switches direction&#13;Ctrl Key repeats"/>-->
</Grid>
</UserControl>

View file

@ -59,7 +59,7 @@ namespace FancyZonesEditor
set { SetValue(IsSelectedProperty, value); }
}
public GridZone()
public GridZone(int spacing)
{
InitializeComponent();
OnSelectionChanged();
@ -69,6 +69,9 @@ namespace FancyZonesEditor
};
Body.Children.Add(_splitter);
Spacing = spacing;
SplitterThickness = Math.Max(spacing, 1);
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
@ -104,19 +107,9 @@ namespace FancyZonesEditor
}
}
private int SplitterThickness
{
get
{
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (!settings.ShowSpacing)
{
return 1;
}
private int Spacing { get; set; }
return Math.Max(settings.Spacing, 1);
}
}
private int SplitterThickness { get; set; }
private void UpdateSplitter()
{
@ -282,14 +275,7 @@ namespace FancyZonesEditor
private void DoSplit(Orientation orientation, double offset)
{
int spacing = 0;
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (settings.ShowSpacing)
{
spacing = settings.Spacing;
}
Split?.Invoke(this, new SplitEventArgs(orientation, offset, spacing));
Split?.Invoke(this, new SplitEventArgs(orientation, offset, Spacing));
}
private void FullSplit_Click(object sender, RoutedEventArgs e)

View file

@ -5,9 +5,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Title="FancyZones Layout" Height="450" Width="800"
Title="FancyZones Layout"
Height="450"
Width="800"
ShowInTaskbar="False"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"/>
Background="{DynamicResource BackdropBrush}"/>

View file

@ -6,8 +6,12 @@
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Loaded="OnLoaded"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="Body">
d:DesignHeight="450"
d:DesignWidth="800">
<Grid>
<Border CornerRadius="4"
Background="{DynamicResource LayoutPreviewBackgroundBrush}" />
<Grid x:Name="Body"
Background="Transparent" />
</Grid>
</UserControl>

View file

@ -8,7 +8,6 @@ using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
@ -22,10 +21,11 @@ namespace FancyZonesEditor
private const string PropertyZoneCountID = "ZoneCount";
private const string PropertyShowSpacingID = "ShowSpacing";
private const string PropertySpacingID = "Spacing";
private const string PropertyZoneBackgroundID = "ZoneBackground";
private const string PropertyZoneBorderID = "ZoneBorder";
private const string ObjectDependencyID = "IsActualSize";
public static readonly DependencyProperty IsActualSizeProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(LayoutPreview), new PropertyMetadata(false));
private LayoutModel _model;
private List<Int32Rect> _zones = new List<Int32Rect>();
@ -49,8 +49,17 @@ namespace FancyZonesEditor
private void LayoutPreview_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_model != null)
{
_model.PropertyChanged -= LayoutModel_PropertyChanged;
}
_model = (LayoutModel)DataContext;
RenderPreview();
if (_model != null)
{
_model.PropertyChanged += LayoutModel_PropertyChanged;
RenderPreview();
}
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
@ -68,6 +77,11 @@ namespace FancyZonesEditor
}
}
private void LayoutModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
RenderPreview();
}
public Int32Rect[] GetZoneRects()
{
return _zones.ToArray();
@ -114,9 +128,7 @@ namespace FancyZonesEditor
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
select new RowColInfo(percent)).ToArray();
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
int spacing = settings.ShowSpacing ? settings.Spacing : 0;
int spacing = grid.ShowSpacing ? grid.Spacing : 0;
var workArea = App.Overlay.WorkArea;
double width = workArea.Width - (spacing * (cols + 1));
@ -157,7 +169,7 @@ namespace FancyZonesEditor
((col == 0) || (grid.CellChildMap[row, col - 1] != childIndex)))
{
// this is not a continuation of a span
Rectangle rect = new Rectangle();
Border rect = new Border();
left = colInfo[col].Start;
top = rowInfo[row].Start;
Canvas.SetTop(rect, top);
@ -177,9 +189,7 @@ namespace FancyZonesEditor
rect.Width = Math.Max(0, colInfo[maxCol].End - left);
rect.Height = Math.Max(0, rowInfo[maxRow].End - top);
rect.StrokeThickness = 1;
rect.Stroke = Brushes.DarkGray;
rect.Fill = Brushes.LightGray;
rect.Style = (Style)FindResource("GridLayoutPreviewActualSizeStyle");
frame.Children.Add(rect);
_zones.Add(new Int32Rect(
(int)left, (int)top, (int)rect.Width, (int)rect.Height));
@ -216,8 +226,7 @@ namespace FancyZonesEditor
Body.ColumnDefinitions.Add(def);
}
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
Thickness margin = new Thickness(settings.ShowSpacing ? settings.Spacing / 20 : 0);
Thickness margin = new Thickness(grid.ShowSpacing ? grid.Spacing / 20 : 0);
List<int> visited = new List<int>();
@ -229,7 +238,7 @@ namespace FancyZonesEditor
if (!visited.Contains(childIndex))
{
visited.Add(childIndex);
Rectangle rect = new Rectangle();
Border rect = new Border();
Grid.SetRow(rect, row);
Grid.SetColumn(rect, col);
int span = 1;
@ -251,11 +260,8 @@ namespace FancyZonesEditor
}
Grid.SetColumnSpan(rect, span);
rect.Margin = margin;
rect.StrokeThickness = 1;
rect.Stroke = Brushes.DarkGray;
rect.Fill = Brushes.LightGray;
rect.Style = (Style)FindResource("GridLayoutPreviewStyle");
Body.Children.Add(rect);
}
}
@ -296,14 +302,21 @@ namespace FancyZonesEditor
foreach (Int32Rect zone in canvas.Zones)
{
Rectangle rect = new Rectangle();
Border rect = new Border();
Canvas.SetTop(rect, zone.Y);
Canvas.SetLeft(rect, zone.X);
rect.MinWidth = zone.Width;
rect.MinHeight = zone.Height;
rect.StrokeThickness = 5;
rect.Stroke = Brushes.DarkGray;
rect.Fill = Brushes.LightGray;
if (IsActualSize)
{
rect.Style = (Style)FindResource("CanvasLayoutPreviewActualSizeStyle");
}
else
{
rect.Style = (Style)FindResource("CanvasLayoutPreviewStyle");
}
frame.Children.Add(rect);
}

File diff suppressed because it is too large Load diff

View file

@ -3,40 +3,32 @@
// 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 System.Windows.Input;
using FancyZonesEditor.Models;
using MahApps.Metro.Controls;
using FancyZonesEditor.Utils;
using ModernWpf.Controls;
namespace FancyZonesEditor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : MetroWindow
public partial class MainWindow : Window
{
// TODO: share the constants b/w C# Editor and FancyZoneLib
public const int MaxZones = 40;
private const int DefaultWrapPanelItemSize = 262;
private const int SmallWrapPanelItemSize = 180;
private const int DefaultWrapPanelItemSize = 164;
private const int SmallWrapPanelItemSize = 164;
private const int MinimalForDefaultWrapPanelsHeight = 900;
private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings;
private LayoutModel _backup = null;
// Localizable string
private static readonly string _defaultNamePrefix = "Custom Layout ";
private ContentDialog _openedDialog = null;
public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize;
public double SettingsTextMaxWidth
{
get
{
return (Width / 2) - 60;
}
}
public MainWindow(bool spanZonesAcrossMonitors, Rect workArea)
{
InitializeComponent();
@ -51,59 +43,84 @@ namespace FancyZonesEditor
if (workArea.Height < MinimalForDefaultWrapPanelsHeight || App.Overlay.MultiMonitorMode)
{
SizeToContent = SizeToContent.WidthAndHeight;
WrapPanelItemSize = SmallWrapPanelItemSize;
}
SizeToContent = SizeToContent.WidthAndHeight;
}
public void Update()
{
DataContext = _settings;
SetSelectedItem();
}
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
OnClosing(sender, null);
if (_openedDialog != null)
{
_openedDialog.Hide();
}
else
{
OnClosing(sender, null);
}
}
}
private void DecrementZones_Click(object sender, RoutedEventArgs e)
{
if (_settings.ZoneCount > 1)
var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
_settings.ZoneCount--;
return;
}
if (model.TemplateZoneCount > 1)
{
model.TemplateZoneCount--;
}
}
private void IncrementZones_Click(object sender, RoutedEventArgs e)
{
if (_settings.ZoneCount < MaxZones)
var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
_settings.ZoneCount++;
return;
}
if (model.TemplateZoneCount < LayoutSettings.MaxZones)
{
model.TemplateZoneCount++;
}
}
private void NewCustomLayoutButton_Click(object sender, RoutedEventArgs e)
private void LayoutItem_MouseEnter(object sender, MouseEventArgs e)
{
WindowLayout window = new WindowLayout();
window.Show();
Hide();
// Select(((Grid)sender).DataContext as LayoutModel);
}
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
{
Select(((Border)sender).DataContext as LayoutModel);
LayoutModel selectedLayoutModel = ((Grid)sender).DataContext as LayoutModel;
Select(selectedLayoutModel);
Apply();
}
private void LayoutItem_Focused(object sender, RoutedEventArgs e)
{
// Ignore focus on Edit button click
if (e.Source is Button)
{
return;
}
Select(((Border)sender).DataContext as LayoutModel);
}
private void LayoutItem_Apply(object sender, KeyEventArgs e)
private void LayoutItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Space)
{
@ -115,17 +132,39 @@ namespace FancyZonesEditor
private void Select(LayoutModel newSelection)
{
if (App.Overlay.CurrentDataContext is LayoutModel currentSelection)
{
currentSelection.IsSelected = false;
}
newSelection.IsSelected = true;
_settings.SetSelectedModel(newSelection);
App.Overlay.CurrentDataContext = newSelection;
}
private void EditLayout_Click(object sender, RoutedEventArgs e)
private async void NewLayoutButton_Click(object sender, RoutedEventArgs e)
{
string defaultNamePrefix = FancyZonesEditor.Properties.Resources.Default_Custom_Layout_Name;
int maxCustomIndex = 0;
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
{
string name = customModel.Name;
if (name.StartsWith(defaultNamePrefix))
{
if (int.TryParse(name.Substring(defaultNamePrefix.Length), out int i))
{
if (maxCustomIndex < i)
{
maxCustomIndex = i;
}
}
}
}
LayoutNameText.Text = defaultNamePrefix + " " + (++maxCustomIndex);
GridLayoutRadioButton.IsChecked = true;
GridLayoutRadioButton.Focus();
await NewLayoutDialog.ShowAsync();
}
private void DuplicateLayout_Click(object sender, RoutedEventArgs e)
{
EditLayoutDialog.Hide();
var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
@ -133,26 +172,32 @@ namespace FancyZonesEditor
}
model.IsSelected = false;
Hide();
bool isPredefinedLayout = MainWindowSettingsModel.IsPredefinedLayout(model);
// make a copy
model = model.Clone();
mainEditor.CurrentDataContext = model;
if (!MainWindowSettingsModel.CustomModels.Contains(model) || isPredefinedLayout)
string name = model.Name;
var index = name.LastIndexOf('(');
if (index != -1)
{
if (isPredefinedLayout)
{
// make a copy
model = model.Clone();
mainEditor.CurrentDataContext = model;
}
name = name.Remove(index);
name = name.TrimEnd();
}
int maxCustomIndex = 0;
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
int maxCustomIndex = 0;
foreach (LayoutModel customModel in MainWindowSettingsModel.CustomModels)
{
string customModelName = customModel.Name;
if (customModelName.StartsWith(name))
{
string name = customModel.Name;
if (name.StartsWith(_defaultNamePrefix))
int openBraceIndex = customModelName.LastIndexOf('(');
int closeBraceIndex = customModelName.LastIndexOf(')');
if (openBraceIndex != -1 && closeBraceIndex != -1)
{
if (int.TryParse(name.Substring(_defaultNamePrefix.Length), out int i))
string indexSubstring = customModelName.Substring(openBraceIndex + 1, closeBraceIndex - openBraceIndex - 1);
if (int.TryParse(indexSubstring, out int i))
{
if (maxCustomIndex < i)
{
@ -161,67 +206,70 @@ namespace FancyZonesEditor
}
}
}
model.Name = _defaultNamePrefix + (++maxCustomIndex);
}
mainEditor.OpenEditor(model);
}
model.Name = name + " (" + (++maxCustomIndex) + ')';
private void Apply_Click(object sender, RoutedEventArgs e)
{
Apply();
model.Persist();
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
App.FancyZonesEditorIO.SerializeZoneSettings();
}
private void Apply()
{
((App)Application.Current).MainWindowSettings.ResetAppliedModel();
var mainEditor = App.Overlay;
if (mainEditor.CurrentDataContext is LayoutModel model)
{
model.Apply();
}
if (!mainEditor.MultiMonitorMode)
{
Close();
_settings.SetAppliedModel(model);
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
App.FancyZonesEditorIO.SerializeZoneSettings();
}
}
private void OnClosing(object sender, EventArgs e)
{
LayoutModel.SerializeDeletedCustomZoneSets();
App.FancyZonesEditorIO.SerializeZoneSettings();
App.Overlay.CloseLayoutWindow();
App.Current.Shutdown();
}
private void OnInitialized(object sender, EventArgs e)
private void DeleteLayout_Click(object sender, RoutedEventArgs e)
{
SetSelectedItem();
EditLayoutDialog.Hide();
DeleteLayout((FrameworkElement)sender);
}
private void SetSelectedItem()
private async void EditLayout_Click(object sender, RoutedEventArgs e)
{
foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
var dataContext = ((FrameworkElement)sender).DataContext;
Select((LayoutModel)dataContext);
if (_settings.SelectedModel is GridLayoutModel grid)
{
if (model.IsSelected)
{
TemplateTab.SelectedItem = model;
return;
}
_backup = new GridLayoutModel(grid);
}
else if (_settings.SelectedModel is CanvasLayoutModel canvas)
{
_backup = new CanvasLayoutModel(canvas);
}
await EditLayoutDialog.ShowAsync();
}
private void OnDelete(object sender, RoutedEventArgs e)
private void EditZones_Click(object sender, RoutedEventArgs e)
{
LayoutModel model = ((FrameworkElement)sender).DataContext as LayoutModel;
if (model.IsSelected)
EditLayoutDialog.Hide();
var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
SetSelectedItem();
return;
}
model.Delete();
_settings.SetSelectedModel(model);
Hide();
mainEditor.OpenEditor(model);
}
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
@ -239,32 +287,129 @@ namespace FancyZonesEditor
e.Handled = true;
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
private void NewLayoutDialog_PrimaryButtonClick(ModernWpf.Controls.ContentDialog sender, ModernWpf.Controls.ContentDialogButtonClickEventArgs args)
{
this.Close();
LayoutModel selectedLayoutModel;
if (GridLayoutRadioButton.IsChecked == true)
{
GridLayoutModel gridModel = new GridLayoutModel(LayoutNameText.Text, LayoutType.Custom)
{
Rows = 1,
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
selectedLayoutModel = gridModel;
}
else
{
selectedLayoutModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Custom)
{
TemplateZoneCount = 0,
};
}
selectedLayoutModel.InitTemplateZones();
App.Overlay.CurrentDataContext = selectedLayoutModel;
var mainEditor = App.Overlay;
Hide();
mainEditor.OpenEditor(selectedLayoutModel);
}
private void Reset_Click(object sender, RoutedEventArgs e)
// This is required to fix a WPF rendering bug when using custom chrome
private void OnContentRendered(object sender, EventArgs e)
{
var overlay = App.Overlay;
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
InvalidateVisual();
}
if (overlay.CurrentDataContext is LayoutModel model)
private void MonitorItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Space)
{
model.IsSelected = false;
model.IsApplied = false;
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
}
}
private void MonitorItem_MouseDown(object sender, MouseButtonEventArgs e)
{
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
}
// EditLayout: Cancel changes
private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
// restore model properties from settings
_settings.RestoreSelectedModel(_backup);
_backup = null;
Select(_settings.AppliedModel);
}
// EditLayout: Save changes
private void EditLayoutDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model))
{
return;
}
overlay.CurrentLayoutSettings.ZonesetUuid = settings.BlankModel.Uuid;
overlay.CurrentLayoutSettings.Type = LayoutType.Blank;
overlay.CurrentDataContext = settings.BlankModel;
_backup = null;
App.FancyZonesEditorIO.SerializeAppliedLayouts();
if (!overlay.MultiMonitorMode)
// update current settings
if (model == _settings.AppliedModel)
{
Close();
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
}
App.FancyZonesEditorIO.SerializeZoneSettings();
// reset selected model
Select(_settings.AppliedModel);
}
private async void DeleteLayout(FrameworkElement element)
{
var dialog = new ModernWpf.Controls.ContentDialog()
{
Title = FancyZonesEditor.Properties.Resources.Are_You_Sure,
Content = FancyZonesEditor.Properties.Resources.Are_You_Sure_Description,
PrimaryButtonText = FancyZonesEditor.Properties.Resources.Delete,
SecondaryButtonText = FancyZonesEditor.Properties.Resources.Cancel,
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
LayoutModel model = element.DataContext as LayoutModel;
if (model == _settings.AppliedModel)
{
_settings.SetAppliedModel(_settings.BlankModel);
Select(_settings.BlankModel);
}
foreach (var monitor in App.Overlay.Monitors)
{
if (monitor.Settings.ZonesetUuid == model.Uuid)
{
App.Overlay.SetLayoutSettings(monitor, _settings.BlankModel);
}
}
App.FancyZonesEditorIO.SerializeZoneSettings();
model.Delete();
}
}
private void Dialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args)
{
_openedDialog = sender;
}
private void Dialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
_openedDialog = null;
}
}
}

View file

@ -2,9 +2,7 @@
// 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.Text.Json;
using System.Windows;
namespace FancyZonesEditor.Models
@ -14,7 +12,7 @@ namespace FancyZonesEditor.Models
public class CanvasLayoutModel : LayoutModel
{
// Non-localizable strings
private const string ModelTypeID = "canvas";
public const string ModelTypeID = "canvas";
public Rect CanvasRect { get; private set; }
@ -35,6 +33,17 @@ namespace FancyZonesEditor.Models
{
}
public CanvasLayoutModel(CanvasLayoutModel other)
: base(other)
{
CanvasRect = new Rect(other.CanvasRect.X, other.CanvasRect.Y, other.CanvasRect.Width, other.CanvasRect.Height);
foreach (Int32Rect zone in other.Zones)
{
Zones.Add(zone);
}
}
// Zones - the list of all zones in this layout, described as independent rectangles
public IList<Int32Rect> Zones { get; private set; } = new List<Int32Rect>();
@ -54,6 +63,28 @@ namespace FancyZonesEditor.Models
UpdateLayout();
}
// InitTemplateZones
// Creates zones based on template zones count
public override void InitTemplateZones()
{
Zones.Clear();
var workingArea = App.Overlay.WorkArea;
int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers
int width = (int)(workingArea.Width * 0.4);
int height = (int)(workingArea.Height * 0.4);
Int32Rect focusZoneRect = new Int32Rect(topLeftCoordinate, topLeftCoordinate, width, height);
int focusRectXIncrement = (TemplateZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); // TODO: replace magic numbers
int focusRectYIncrement = (TemplateZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50); // TODO: replace magic numbers
for (int i = 0; i < TemplateZoneCount; i++)
{
Zones.Add(focusZoneRect);
focusZoneRect.X += focusRectXIncrement;
focusZoneRect.Y += focusRectYIncrement;
}
}
private void UpdateLayout()
{
FirePropertyChanged();
@ -71,6 +102,7 @@ namespace FancyZonesEditor.Models
layout.Zones.Add(zone);
}
layout.SensitivityRadius = SensitivityRadius;
return layout;
}
@ -81,37 +113,8 @@ namespace FancyZonesEditor.Models
{
other.Zones.Add(zone);
}
}
private struct Zone
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
private struct CanvasLayoutInfo
{
public int RefWidth { get; set; }
public int RefHeight { get; set; }
public Zone[] Zones { get; set; }
}
private struct CanvasLayoutJson
{
public string Uuid { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public CanvasLayoutInfo Info { get; set; }
other.SensitivityRadius = SensitivityRadius;
}
// PersistData
@ -119,48 +122,6 @@ namespace FancyZonesEditor.Models
protected override void PersistData()
{
AddCustomLayout(this);
var canvasRect = CanvasRect;
if (canvasRect.Width == 0 || canvasRect.Height == 0)
{
canvasRect = App.Overlay.WorkArea;
}
CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo
{
RefWidth = (int)canvasRect.Width,
RefHeight = (int)canvasRect.Height,
Zones = new Zone[Zones.Count],
};
for (int i = 0; i < Zones.Count; ++i)
{
Zone zone = new Zone
{
X = Zones[i].X,
Y = Zones[i].Y,
Width = Zones[i].Width,
Height = Zones[i].Height,
};
layoutInfo.Zones[i] = zone;
}
CanvasLayoutJson jsonObj = new CanvasLayoutJson
{
Uuid = Uuid,
Name = Name,
Type = ModelTypeID,
Info = layoutInfo,
};
JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
};
string jsonString = JsonSerializer.Serialize(jsonObj, options);
AddCustomLayoutJson(JsonSerializer.Deserialize<JsonElement>(jsonString));
SerializeCreatedCustomZonesets();
}
}
}

View file

@ -1,11 +1,8 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Windows;
namespace FancyZonesEditor.Models
{
@ -14,7 +11,25 @@ namespace FancyZonesEditor.Models
public class GridLayoutModel : LayoutModel
{
// Non-localizable strings
private const string ModelTypeID = "grid";
public const string ModelTypeID = "grid";
public const int GridMultiplier = 10000;
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
private static readonly byte[][] _priorityData = new byte[][]
{
new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 },
new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 },
new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 },
};
// Rows - number of rows in the Grid
public int Rows
@ -29,7 +44,6 @@ namespace FancyZonesEditor.Models
if (_rows != value)
{
_rows = value;
FirePropertyChanged();
}
}
}
@ -49,7 +63,6 @@ namespace FancyZonesEditor.Models
if (_cols != value)
{
_cols = value;
FirePropertyChanged();
}
}
}
@ -62,10 +75,50 @@ namespace FancyZonesEditor.Models
public int[,] CellChildMap { get; set; }
// RowPercents - represents the %age height of each row in the grid
public List<int> RowPercents { get; set; }
public List<int> RowPercents { get; set; } = new List<int>();
// ColumnPercents - represents the %age width of each column in the grid
public List<int> ColumnPercents { get; set; }
public List<int> ColumnPercents { get; set; } = new List<int>();
// ShowSpacing - flag if free space between cells should be presented
public bool ShowSpacing
{
get
{
return _showSpacing;
}
set
{
if (value != _showSpacing)
{
_showSpacing = value;
FirePropertyChanged(nameof(ShowSpacing));
}
}
}
private bool _showSpacing = LayoutSettings.DefaultShowSpacing;
// Spacing - free space between cells
public int Spacing
{
get
{
return _spacing;
}
set
{
if (value != _spacing)
{
_spacing = value;
FirePropertyChanged(nameof(Spacing));
}
}
}
private int _spacing = LayoutSettings.DefaultSpacing;
// FreeZones (not persisted) - used to keep track of child indices that are no longer in use in the CellChildMap,
// making them candidates for re-use when it's needed to add another child
@ -97,13 +150,46 @@ namespace FancyZonesEditor.Models
CellChildMap = cellChildMap;
}
public GridLayoutModel(GridLayoutModel other)
: base(other)
{
_rows = other._rows;
_cols = other._cols;
_showSpacing = other._showSpacing;
_spacing = other._spacing;
CellChildMap = new int[_rows, _cols];
for (int row = 0; row < _rows; row++)
{
for (int col = 0; col < _cols; col++)
{
CellChildMap[row, col] = other.CellChildMap[row, col];
}
}
for (int row = 0; row < _rows; row++)
{
RowPercents.Add(other.RowPercents[row]);
}
for (int col = 0; col < _cols; col++)
{
ColumnPercents.Add(other.ColumnPercents[col]);
}
}
public void UpdatePreview()
{
FirePropertyChanged();
}
public void Reload(byte[] data)
{
// Skip version (2 bytes), id (2 bytes), and type (1 bytes)
int i = 5;
Rows = data[i++];
Columns = data[i++];
_rows = data[i++];
_cols = data[i++];
RowPercents = new List<int>(Rows);
for (int row = 0; row < Rows; row++)
@ -125,6 +211,8 @@ namespace FancyZonesEditor.Models
CellChildMap[row, col] = data[i++];
}
}
FirePropertyChanged();
}
// Clone
@ -171,30 +259,36 @@ namespace FancyZonesEditor.Models
}
layout.ColumnPercents = colPercents;
layout.ShowSpacing = ShowSpacing;
layout.Spacing = Spacing;
layout.SensitivityRadius = SensitivityRadius;
}
private struct GridLayoutInfo
// InitTemplateZones
// Creates zones based on template zones count
public override void InitTemplateZones()
{
public int Rows { get; set; }
switch (Type)
{
case LayoutType.Rows:
InitRows();
break;
case LayoutType.Columns:
InitColumns();
break;
case LayoutType.Grid:
InitGrid();
break;
case LayoutType.PriorityGrid:
InitPriorityGrid();
break;
case LayoutType.Custom:
InitColumns(); // Custom is initialized with columns
break;
}
public int Columns { get; set; }
public List<int> RowsPercentage { get; set; }
public List<int> ColumnsPercentage { get; set; }
public int[][] CellChildMap { get; set; }
}
private struct GridLayoutJson
{
public string Uuid { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public GridLayoutInfo Info { get; set; }
FirePropertyChanged();
}
// PersistData
@ -202,40 +296,105 @@ namespace FancyZonesEditor.Models
protected override void PersistData()
{
AddCustomLayout(this);
}
GridLayoutInfo layoutInfo = new GridLayoutInfo
{
Rows = Rows,
Columns = Columns,
RowsPercentage = RowPercents,
ColumnsPercentage = ColumnPercents,
CellChildMap = new int[Rows][],
};
private void InitRows()
{
CellChildMap = new int[TemplateZoneCount, 1];
RowPercents = new List<int>(TemplateZoneCount);
for (int row = 0; row < Rows; row++)
for (int i = 0; i < TemplateZoneCount; i++)
{
layoutInfo.CellChildMap[row] = new int[Columns];
for (int col = 0; col < Columns; col++)
CellChildMap[i, 0] = i;
// Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make
// the sum of all RowPercents exactly (_multiplier).
RowPercents.Add(((GridMultiplier * (i + 1)) / TemplateZoneCount) - ((GridMultiplier * i) / TemplateZoneCount));
}
_rows = TemplateZoneCount;
}
private void InitColumns()
{
CellChildMap = new int[1, TemplateZoneCount];
ColumnPercents = new List<int>(TemplateZoneCount);
for (int i = 0; i < TemplateZoneCount; i++)
{
CellChildMap[0, i] = i;
// Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make
// the sum of all RowPercents exactly (_multiplier).
ColumnPercents.Add(((GridMultiplier * (i + 1)) / TemplateZoneCount) - ((GridMultiplier * i) / TemplateZoneCount));
}
_cols = TemplateZoneCount;
}
private void InitGrid()
{
int rows = 1;
while (TemplateZoneCount / rows >= rows)
{
rows++;
}
rows--;
int cols = TemplateZoneCount / rows;
if (TemplateZoneCount % rows == 0)
{
// even grid
}
else
{
cols++;
}
RowPercents = new List<int>(rows);
ColumnPercents = new List<int>(cols);
CellChildMap = new int[rows, cols];
// Note: The following are NOT equal to _multiplier divided by rows or columns and is
// done like this to make the sum of all RowPercents exactly (_multiplier).
for (int row = 0; row < rows; row++)
{
RowPercents.Add(((GridMultiplier * (row + 1)) / rows) - ((GridMultiplier * row) / rows));
}
for (int col = 0; col < cols; col++)
{
ColumnPercents.Add(((GridMultiplier * (col + 1)) / cols) - ((GridMultiplier * col) / cols));
}
int index = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
layoutInfo.CellChildMap[row][col] = CellChildMap[row, col];
CellChildMap[row, col] = index++;
if (index == TemplateZoneCount)
{
index--;
}
}
}
GridLayoutJson jsonObj = new GridLayoutJson
{
Uuid = Uuid,
Name = Name,
Type = ModelTypeID,
Info = layoutInfo,
};
JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
};
_rows = rows;
_cols = cols;
}
string jsonString = JsonSerializer.Serialize(jsonObj, options);
AddCustomLayoutJson(JsonSerializer.Deserialize<JsonElement>(jsonString));
SerializeCreatedCustomZonesets();
private void InitPriorityGrid()
{
if (TemplateZoneCount <= _priorityData.Length)
{
Reload(_priorityData[TemplateZoneCount - 1]);
}
else
{
// same as grid;
InitGrid();
}
}
}
}

View file

@ -3,11 +3,8 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json;
namespace FancyZonesEditor.Models
{
@ -42,6 +39,17 @@ namespace FancyZonesEditor.Models
Type = type;
}
protected LayoutModel(LayoutModel other)
{
_guid = other._guid;
_name = other._name;
Type = other.Type;
_isSelected = other._isSelected;
_isApplied = other._isApplied;
_sensitivityRadius = other._sensitivityRadius;
_zoneCount = other._zoneCount;
}
// Name - the display name for this layout model - is also used as the key in the registry
public string Name
{
@ -55,7 +63,7 @@ namespace FancyZonesEditor.Models
if (_name != value)
{
_name = value;
FirePropertyChanged();
FirePropertyChanged(nameof(Name));
}
}
}
@ -82,6 +90,14 @@ namespace FancyZonesEditor.Models
}
}
public bool IsCustom
{
get
{
return Type == LayoutType.Custom;
}
}
// IsSelected (not-persisted) - tracks whether or not this LayoutModel is selected in the picker
// TODO: once we switch to a picker per monitor, we need to move this state to the view
public bool IsSelected
@ -96,13 +112,14 @@ namespace FancyZonesEditor.Models
if (_isSelected != value)
{
_isSelected = value;
FirePropertyChanged();
FirePropertyChanged(nameof(IsSelected));
}
}
}
private bool _isSelected;
// IsApplied (not-persisted) - tracks whether or not this LayoutModel is applied in the picker
public bool IsApplied
{
get
@ -115,13 +132,53 @@ namespace FancyZonesEditor.Models
if (_isApplied != value)
{
_isApplied = value;
FirePropertyChanged();
FirePropertyChanged(nameof(IsApplied));
}
}
}
private bool _isApplied;
public int SensitivityRadius
{
get
{
return _sensitivityRadius;
}
set
{
if (value != _sensitivityRadius)
{
_sensitivityRadius = value;
FirePropertyChanged(nameof(SensitivityRadius));
}
}
}
private int _sensitivityRadius = LayoutSettings.DefaultSensitivityRadius;
// TemplateZoneCount - number of zones selected in the picker window for template layouts
public int TemplateZoneCount
{
get
{
return _zoneCount;
}
set
{
if (value != _zoneCount)
{
_zoneCount = value;
InitTemplateZones();
FirePropertyChanged(nameof(TemplateZoneCount));
}
}
}
private int _zoneCount = LayoutSettings.DefaultZoneCount;
// implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
@ -134,11 +191,11 @@ namespace FancyZonesEditor.Models
// Removes this Layout from the registry and the loaded CustomModels list
public void Delete()
{
int i = _customModels.IndexOf(this);
var customModels = MainWindowSettingsModel.CustomModels;
int i = customModels.IndexOf(this);
if (i != -1)
{
_customModels.RemoveAt(i);
_deletedCustomModels.Add(Guid.ToString().ToUpper());
customModels.RemoveAt(i);
}
}
@ -146,48 +203,25 @@ namespace FancyZonesEditor.Models
public void AddCustomLayout(LayoutModel model)
{
bool updated = false;
for (int i = 0; i < _customModels.Count && !updated; i++)
var customModels = MainWindowSettingsModel.CustomModels;
for (int i = 0; i < customModels.Count && !updated; i++)
{
if (_customModels[i].Uuid == model.Uuid)
if (customModels[i].Uuid == model.Uuid)
{
_customModels[i] = model;
customModels[i] = model;
updated = true;
}
}
if (!updated)
{
_customModels.Add(model);
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()
{
App.FancyZonesEditorIO.SerializeDeletedCustomZoneSets(_deletedCustomModels);
}
public static void SerializeCreatedCustomZonesets()
{
App.FancyZonesEditorIO.SerializeCreatedCustomZonesets(_createdCustomLayouts);
}
// Loads all the custom Layouts from tmp file passed by FancyZonesLib
public static ObservableCollection<LayoutModel> LoadCustomModels()
{
_customModels = new ObservableCollection<LayoutModel>();
App.FancyZonesEditorIO.ParseLayouts(ref _customModels, ref _deletedCustomModels);
return _customModels;
}
private static ObservableCollection<LayoutModel> _customModels;
private static List<string> _deletedCustomModels = new List<string>();
private static List<JsonElement> _createdCustomLayouts = new List<JsonElement>();
// InitTemplateZones
// Creates zones based on template zones count
public abstract void InitTemplateZones();
// Callbacks that the base LayoutModel makes to derived types
protected abstract void PersistData();
@ -197,21 +231,6 @@ namespace FancyZonesEditor.Models
public void Persist()
{
PersistData();
Apply();
}
public void Apply()
{
MainWindowSettingsModel settings = ((App)App.Current).MainWindowSettings;
settings.ResetAppliedModel();
IsApplied = true;
// update settings
App.Overlay.CurrentLayoutSettings.ZonesetUuid = Uuid;
App.Overlay.CurrentLayoutSettings.Type = Type;
// update temp file
App.FancyZonesEditorIO.SerializeAppliedLayouts();
}
}
}

View file

@ -9,13 +9,16 @@ namespace FancyZonesEditor
{
public class LayoutSettings
{
public static bool DefaultShowSpacing => true;
// TODO: share the constants b/w C# Editor and FancyZoneLib
public const bool DefaultShowSpacing = true;
public static int DefaultSpacing => 16;
public const int DefaultSpacing = 16;
public static int DefaultZoneCount => 3;
public const int DefaultZoneCount = 3;
public static int DefaultSensitivityRadius => 20;
public const int DefaultSensitivityRadius = 20;
public const int MaxZones = 40;
public string ZonesetUuid { get; set; } = string.Empty;

View file

@ -2,12 +2,10 @@
// 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.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
@ -25,43 +23,17 @@ namespace FancyZonesEditor
VirtualDesktopId,
}
private static CanvasLayoutModel _blankCustomModel;
private readonly CanvasLayoutModel _blankModel;
private readonly CanvasLayoutModel _focusModel;
private readonly GridLayoutModel _rowsModel;
private readonly GridLayoutModel _columnsModel;
private readonly GridLayoutModel _gridModel;
private readonly GridLayoutModel _priorityGridModel;
public const ushort _focusModelId = 0xFFFF;
public const ushort _rowsModelId = 0xFFFE;
public const ushort _columnsModelId = 0xFFFD;
public const ushort _gridModelId = 0xFFFC;
public const ushort _priorityGridModelId = 0xFFFB;
public const ushort _blankCustomModelId = 0xFFFA;
public const ushort _lastDefinedId = _blankCustomModelId;
// Non-localizable strings
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
// hard coded data for all the "Priority Grid" configurations that are unique to "Grid"
private static readonly byte[][] _priorityData = new byte[][]
{
new byte[] { 0, 0, 0, 0, 0, 1, 1, 39, 16, 39, 16, 0 },
new byte[] { 0, 0, 0, 0, 0, 1, 2, 39, 16, 26, 11, 13, 5, 0, 1 },
new byte[] { 0, 0, 0, 0, 0, 1, 3, 39, 16, 9, 196, 19, 136, 9, 196, 0, 1, 2 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3 },
new byte[] { 0, 0, 0, 0, 0, 2, 3, 19, 136, 19, 136, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 0, 1, 3, 4, 1, 5 },
new byte[] { 0, 0, 0, 0, 0, 3, 3, 13, 5, 13, 6, 13, 5, 9, 196, 19, 136, 9, 196, 0, 1, 2, 3, 1, 4, 5, 1, 6 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 1, 8, 9 },
new byte[] { 0, 0, 0, 0, 0, 3, 4, 13, 5, 13, 6, 13, 5, 9, 196, 9, 196, 9, 196, 9, 196, 0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9, 10 },
};
private const int _multiplier = 10000;
public bool IsCustomLayoutActive
{
get
@ -80,112 +52,41 @@ namespace FancyZonesEditor
public MainWindowSettingsModel()
{
// Initialize the five default layout models: Focus, Columns, Rows, Grid, and PriorityGrid
DefaultModels = new List<LayoutModel>(5);
// Initialize default layout models: Blank, Focus, Columns, Rows, Grid, and PriorityGrid
_blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank)
{
TemplateZoneCount = 0,
SensitivityRadius = 0,
};
DefaultModels.Add(_blankModel);
_focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
_focusModel.InitTemplateZones();
DefaultModels.Add(_focusModel);
_columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
{
Rows = 1,
RowPercents = new List<int>(1) { _multiplier },
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_columnsModel.InitTemplateZones();
DefaultModels.Add(_columnsModel);
_rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
{
Columns = 1,
ColumnPercents = new List<int>(1) { _multiplier },
ColumnPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_rowsModel.InitTemplateZones();
DefaultModels.Add(_rowsModel);
_gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
_gridModel.InitTemplateZones();
DefaultModels.Add(_gridModel);
_priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
_priorityGridModel.InitTemplateZones();
DefaultModels.Add(_priorityGridModel);
_blankCustomModel = new CanvasLayoutModel(Properties.Resources.Custom_Layout_Create_New, LayoutType.Blank);
UpdateTemplateLayoutModels();
}
// ZoneCount - number of zones selected in the picker window
public int ZoneCount
{
get
{
return App.Overlay.CurrentLayoutSettings.ZoneCount;
}
set
{
if (App.Overlay.CurrentLayoutSettings.ZoneCount != value)
{
App.Overlay.CurrentLayoutSettings.ZoneCount = value;
UpdateTemplateLayoutModels();
FirePropertyChanged(nameof(ZoneCount));
}
}
}
// Spacing - how much space in between zones of the grid do you want
public int Spacing
{
get
{
return App.Overlay.CurrentLayoutSettings.Spacing;
}
set
{
value = Math.Max(0, value);
if (App.Overlay.CurrentLayoutSettings.Spacing != value)
{
App.Overlay.CurrentLayoutSettings.Spacing = value;
UpdateTemplateLayoutModels();
FirePropertyChanged(nameof(Spacing));
}
}
}
// ShowSpacing - is the Spacing value used or ignored?
public bool ShowSpacing
{
get
{
return App.Overlay.CurrentLayoutSettings.ShowSpacing;
}
set
{
if (App.Overlay.CurrentLayoutSettings.ShowSpacing != value)
{
App.Overlay.CurrentLayoutSettings.ShowSpacing = value;
UpdateTemplateLayoutModels();
FirePropertyChanged(nameof(ShowSpacing));
}
}
}
// SensitivityRadius - how much space inside the zone to highlight the adjacent zone too
public int SensitivityRadius
{
get
{
return App.Overlay.CurrentLayoutSettings.SensitivityRadius;
}
set
{
value = Math.Max(0, value);
if (App.Overlay.CurrentLayoutSettings.SensitivityRadius != value)
{
App.Overlay.CurrentLayoutSettings.SensitivityRadius = value;
UpdateTemplateLayoutModels();
FirePropertyChanged(nameof(SensitivityRadius));
}
}
}
// IsShiftKeyPressed - is the shift key currently being held down
@ -228,138 +129,7 @@ namespace FancyZonesEditor
private bool _isCtrlKeyPressed;
// UpdateLayoutModels
// Update the five default layouts based on the new ZoneCount
private void UpdateTemplateLayoutModels()
{
// Update the "Focus" Default Layout
_focusModel.Zones.Clear();
// Sanity check for imported settings that may have invalid data
if (ZoneCount < 1)
{
ZoneCount = 3;
}
// If changing focus layout zones size and/or increment,
// same change should be applied in ZoneSet.cpp (ZoneSet::CalculateFocusLayout)
var workingArea = App.Overlay.WorkArea;
int topLeftCoordinate = (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(100); // TODO: replace magic numbers
int width = (int)(workingArea.Width * 0.4);
int height = (int)(workingArea.Height * 0.4);
Int32Rect focusZoneRect = new Int32Rect(topLeftCoordinate, topLeftCoordinate, width, height);
int focusRectXIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50);
int focusRectYIncrement = (ZoneCount <= 1) ? 0 : (int)App.Overlay.ScaleCoordinateWithCurrentMonitorDpi(50);
for (int i = 0; i < ZoneCount; i++)
{
_focusModel.Zones.Add(focusZoneRect);
focusZoneRect.X += focusRectXIncrement;
focusZoneRect.Y += focusRectYIncrement;
}
// Update the "Rows" and "Columns" Default Layouts
// They can share their model, just transposed
_rowsModel.CellChildMap = new int[ZoneCount, 1];
_columnsModel.CellChildMap = new int[1, ZoneCount];
_rowsModel.Rows = _columnsModel.Columns = ZoneCount;
_rowsModel.RowPercents = _columnsModel.ColumnPercents = new List<int>(ZoneCount);
for (int i = 0; i < ZoneCount; i++)
{
_rowsModel.CellChildMap[i, 0] = i;
_columnsModel.CellChildMap[0, i] = i;
// Note: This is NOT equal to _multiplier / ZoneCount and is done like this to make
// the sum of all RowPercents exactly (_multiplier).
// _columnsModel is sharing the same array
_rowsModel.RowPercents.Add(((_multiplier * (i + 1)) / ZoneCount) - ((_multiplier * i) / ZoneCount));
}
// Update the "Grid" Default Layout
int rows = 1;
while (ZoneCount / rows >= rows)
{
rows++;
}
rows--;
int cols = ZoneCount / rows;
if (ZoneCount % rows == 0)
{
// even grid
}
else
{
cols++;
}
_gridModel.Rows = rows;
_gridModel.Columns = cols;
_gridModel.RowPercents = new List<int>(rows);
_gridModel.ColumnPercents = new List<int>(cols);
_gridModel.CellChildMap = new int[rows, cols];
// Note: The following are NOT equal to _multiplier divided by rows or columns and is
// done like this to make the sum of all RowPercents exactly (_multiplier).
for (int row = 0; row < rows; row++)
{
_gridModel.RowPercents.Add(((_multiplier * (row + 1)) / rows) - ((_multiplier * row) / rows));
}
for (int col = 0; col < cols; col++)
{
_gridModel.ColumnPercents.Add(((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols));
}
int index = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
_gridModel.CellChildMap[row, col] = index++;
if (index == ZoneCount)
{
index--;
}
}
}
// Update the "Priority Grid" Default Layout
if (ZoneCount <= _priorityData.Length)
{
_priorityGridModel.Reload(_priorityData[ZoneCount - 1]);
}
else
{
// same as grid;
_priorityGridModel.Rows = _gridModel.Rows;
_priorityGridModel.Columns = _gridModel.Columns;
_priorityGridModel.RowPercents = _gridModel.RowPercents;
_priorityGridModel.ColumnPercents = _gridModel.ColumnPercents;
_priorityGridModel.CellChildMap = _gridModel.CellChildMap;
}
}
public IList<LayoutModel> DefaultModels { get; }
public static ObservableCollection<LayoutModel> CustomModels
{
get
{
if (_customModels == null)
{
_customModels = LayoutModel.LoadCustomModels();
_customModels.Insert(0, _blankCustomModel);
}
return _customModels;
}
}
private static ObservableCollection<LayoutModel> _customModels;
public CanvasLayoutModel BlankModel
public LayoutModel BlankModel
{
get
{
@ -367,7 +137,55 @@ namespace FancyZonesEditor
}
}
private CanvasLayoutModel _blankModel = new CanvasLayoutModel(string.Empty, LayoutType.Blank);
public static IList<LayoutModel> DefaultModels { get; } = new List<LayoutModel>(6);
public static ObservableCollection<LayoutModel> CustomModels
{
get
{
return _customModels;
}
}
private static ObservableCollection<LayoutModel> _customModels = new ObservableCollection<LayoutModel>();
public LayoutModel SelectedModel
{
get
{
return _selectedModel;
}
private set
{
if (_selectedModel != value)
{
_selectedModel = value;
FirePropertyChanged(nameof(SelectedModel));
}
}
}
private LayoutModel _selectedModel = null;
public LayoutModel AppliedModel
{
get
{
return _appliedModel;
}
private set
{
if (_appliedModel != value)
{
_appliedModel = value;
FirePropertyChanged(nameof(AppliedModel));
}
}
}
private LayoutModel _appliedModel = null;
public static bool IsPredefinedLayout(LayoutModel model)
{
@ -376,21 +194,13 @@ namespace FancyZonesEditor
public LayoutModel UpdateSelectedLayoutModel()
{
UpdateTemplateLayoutModels();
ResetAppliedModel();
ResetSelectedModel();
LayoutModel foundModel = null;
LayoutSettings currentApplied = App.Overlay.CurrentLayoutSettings;
// set new layout
if (currentApplied.Type == LayoutType.Blank)
if (currentApplied.Type == LayoutType.Custom)
{
foundModel = BlankModel;
}
else if (currentApplied.Type == LayoutType.Custom)
{
foreach (LayoutModel model in MainWindowSettingsModel.CustomModels)
foreach (LayoutModel model in CustomModels)
{
if ("{" + model.Guid.ToString().ToUpperInvariant() + "}" == currentApplied.ZonesetUuid.ToUpperInvariant())
{
@ -408,6 +218,15 @@ namespace FancyZonesEditor
{
// found match
foundModel = model;
foundModel.TemplateZoneCount = currentApplied.ZoneCount;
foundModel.SensitivityRadius = currentApplied.SensitivityRadius;
if (foundModel is GridLayoutModel grid)
{
grid.ShowSpacing = currentApplied.ShowSpacing;
grid.Spacing = currentApplied.Spacing;
}
foundModel.InitTemplateZones();
break;
}
}
@ -415,80 +234,74 @@ namespace FancyZonesEditor
if (foundModel == null)
{
foundModel = DefaultModels[4]; // PriorityGrid
foundModel = _priorityGridModel;
}
foundModel.IsSelected = true;
foundModel.IsApplied = true;
SetSelectedModel(foundModel);
SetAppliedModel(foundModel);
FirePropertyChanged(nameof(IsCustomLayoutActive));
return foundModel;
}
public void ResetSelectedModel()
public void RestoreSelectedModel(LayoutModel model)
{
foreach (LayoutModel model in CustomModels)
if (SelectedModel == null || model == null)
{
if (model.IsSelected)
{
model.IsSelected = false;
break;
}
return;
}
foreach (LayoutModel model in DefaultModels)
SelectedModel.SensitivityRadius = model.SensitivityRadius;
SelectedModel.TemplateZoneCount = model.TemplateZoneCount;
SelectedModel.IsSelected = model.IsSelected;
SelectedModel.IsApplied = model.IsApplied;
SelectedModel.Name = model.Name;
if (model is GridLayoutModel grid)
{
if (model.IsSelected)
{
model.IsSelected = false;
break;
}
((GridLayoutModel)SelectedModel).Spacing = grid.Spacing;
((GridLayoutModel)SelectedModel).ShowSpacing = grid.ShowSpacing;
}
}
public void ResetAppliedModel()
public void SetSelectedModel(LayoutModel model)
{
foreach (LayoutModel model in CustomModels)
if (_selectedModel != null)
{
if (model.IsApplied)
{
model.IsApplied = false;
break;
}
_selectedModel.IsSelected = false;
}
foreach (LayoutModel model in DefaultModels)
if (model != null)
{
if (model.IsApplied)
{
model.IsApplied = false;
break;
}
model.IsSelected = true;
}
SelectedModel = model;
}
public void UpdateDesktopDependantProperties(LayoutSettings prevSettings)
public void SetAppliedModel(LayoutModel model)
{
UpdateTemplateLayoutModels();
if (prevSettings.ZoneCount != ZoneCount)
if (_appliedModel != null)
{
FirePropertyChanged(nameof(ZoneCount));
_appliedModel.IsApplied = false;
}
if (prevSettings.Spacing != Spacing)
if (model != null)
{
FirePropertyChanged(nameof(Spacing));
model.IsApplied = true;
}
if (prevSettings.ShowSpacing != ShowSpacing)
{
FirePropertyChanged(nameof(ShowSpacing));
}
AppliedModel = model;
}
if (prevSettings.SensitivityRadius != SensitivityRadius)
public void UpdateDefaultModels()
{
foreach (LayoutModel model in DefaultModels)
{
FirePropertyChanged(nameof(SensitivityRadius));
if (App.Overlay.CurrentLayoutSettings.Type == model.Type && App.Overlay.CurrentLayoutSettings.ZoneCount != model.TemplateZoneCount)
{
model.TemplateZoneCount = App.Overlay.CurrentLayoutSettings.ZoneCount;
model.InitTemplateZones();
}
}
}

View file

@ -5,7 +5,6 @@
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using FancyZonesEditor.Utils;

View file

@ -13,7 +13,6 @@ namespace FancyZonesEditor
public class Overlay
{
private MainWindow _mainWindow;
private LayoutPreview _layoutPreview;
private UserControl _editor;
@ -28,7 +27,7 @@ namespace FancyZonesEditor
return Monitors[CurrentDesktop].Device.WorkAreaRect;
}
return default(Rect);
return default;
}
}
@ -54,7 +53,7 @@ namespace FancyZonesEditor
return Monitors[CurrentDesktop].Window;
}
return default(Window);
return default;
}
}
@ -100,14 +99,13 @@ namespace FancyZonesEditor
return;
}
var prevSettings = CurrentLayoutSettings;
_currentDesktop = value;
MainWindowSettingsModel settings = ((App)Application.Current).MainWindowSettings;
if (settings != null)
{
settings.ResetAppliedModel();
settings.UpdateDesktopDependantProperties(prevSettings);
settings.SetAppliedModel(null);
settings.UpdateDefaultModels();
}
Update();
@ -130,8 +128,8 @@ namespace FancyZonesEditor
if (_spanZonesAcrossMonitors)
{
Rect workArea = default(Rect);
Rect bounds = default(Rect);
Rect workArea = default;
Rect bounds = default;
foreach (Monitor monitor in Monitors)
{
@ -174,7 +172,7 @@ namespace FancyZonesEditor
_layoutPreview = new LayoutPreview
{
IsActualSize = true,
Opacity = 0.5,
Opacity = 1,
};
ShowLayout();
@ -201,6 +199,30 @@ namespace FancyZonesEditor
}
}
public void SetLayoutSettings(Monitor monitor, LayoutModel model)
{
if (model == null)
{
return;
}
monitor.Settings.ZonesetUuid = model.Uuid;
monitor.Settings.Type = model.Type;
monitor.Settings.SensitivityRadius = model.SensitivityRadius;
monitor.Settings.ZoneCount = model.TemplateZoneCount;
if (model is GridLayoutModel grid)
{
monitor.Settings.ShowSpacing = grid.ShowSpacing;
monitor.Settings.Spacing = grid.Spacing;
}
else
{
monitor.Settings.ShowSpacing = false;
monitor.Settings.Spacing = 0;
}
}
public void OpenEditor(LayoutModel model)
{
_layoutPreview = null;
@ -216,11 +238,10 @@ namespace FancyZonesEditor
CurrentLayoutWindow.Content = _editor;
EditorWindow window;
bool isGrid = false;
if (model is GridLayoutModel)
{
window = new GridEditorWindow();
isGrid = true;
}
else
{
@ -230,14 +251,6 @@ namespace FancyZonesEditor
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()
@ -246,7 +259,7 @@ namespace FancyZonesEditor
_layoutPreview = new LayoutPreview
{
IsActualSize = true,
Opacity = 0.5,
Opacity = 1,
};
CurrentLayoutWindow.Content = _layoutPreview;
@ -318,8 +331,6 @@ namespace FancyZonesEditor
_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

View file

@ -78,6 +78,33 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Apply layout.
/// </summary>
public static string Apply_Layout {
get {
return ResourceManager.GetString("Apply_Layout", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure?.
/// </summary>
public static string Are_You_Sure {
get {
return ResourceManager.GetString("Are_You_Sure", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to delete this layout?.
/// </summary>
public static string Are_You_Sure_Description {
get {
return ResourceManager.GetString("Are_You_Sure_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
@ -97,7 +124,7 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Choose your layout for this desktop.
/// Looks up a localized string similar to Choose the layout for this desktop.
/// </summary>
public static string Choose_Layout {
get {
@ -105,6 +132,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Choose layout type.
/// </summary>
public static string Choose_layout_type {
get {
return ResourceManager.GetString("Choose_layout_type", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Close.
/// </summary>
@ -132,6 +168,33 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Create.
/// </summary>
public static string Create {
get {
return ResourceManager.GetString("Create", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create custom layout.
/// </summary>
public static string Create_Custom_From_Template {
get {
return ResourceManager.GetString("Create_Custom_From_Template", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create new layout.
/// </summary>
public static string Create_new_layout {
get {
return ResourceManager.GetString("Create_new_layout", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom.
/// </summary>
@ -142,7 +205,7 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Create new custom.
/// Looks up a localized string similar to Custom layout.
/// </summary>
public static string Custom_Layout_Create_New {
get {
@ -160,20 +223,29 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Delete custom layout.
/// Looks up a localized string similar to Custom layout.
/// </summary>
public static string Custom_Layout_Delete_Button {
public static string Default_Custom_Layout_Name {
get {
return ResourceManager.GetString("Custom_Layout_Delete_Button", resourceCulture);
return ResourceManager.GetString("Default_Custom_Layout_Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom table layout creator.
/// Looks up a localized string similar to Delete.
/// </summary>
public static string Custom_Table_Layout {
public static string Delete {
get {
return ResourceManager.GetString("Custom_Table_Layout", resourceCulture);
return ResourceManager.GetString("Delete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete zone.
/// </summary>
public static string Delete_Zone {
get {
return ResourceManager.GetString("Delete_Zone", resourceCulture);
}
}
@ -187,11 +259,38 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Edit selected layout.
/// Looks up a localized string similar to Duplicate.
/// </summary>
public static string Edit_Selected_Layout {
public static string Duplicate {
get {
return ResourceManager.GetString("Edit_Selected_Layout", resourceCulture);
return ResourceManager.GetString("Duplicate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
public static string Edit {
get {
return ResourceManager.GetString("Edit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit layout.
/// </summary>
public static string Edit_Layout {
get {
return ResourceManager.GetString("Edit_Layout", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit zones.
/// </summary>
public static string Edit_zones {
get {
return ResourceManager.GetString("Edit_zones", resourceCulture);
}
}
@ -276,6 +375,33 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;zones-settings.json&apos; contains malformed data..
/// </summary>
public static string Error_Parsing_Zones_Settings_Malformed_Data {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Malformed_Data", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Editor settings parsing error..
/// </summary>
public static string Error_Parsing_Zones_Settings_Title {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Would you like to continue? Malformed data will be lost..
/// </summary>
public static string Error_Parsing_Zones_Settings_User_Choice {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_User_Choice", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error persisting custom layout.
/// </summary>
@ -330,6 +456,60 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Create layouts that have overlapping zones.
/// </summary>
public static string Layout_Canvas_Description {
get {
return ResourceManager.GetString("Layout_Canvas_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Canvas.
/// </summary>
public static string Layout_Canvas_Title {
get {
return ResourceManager.GetString("Layout_Canvas_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create layouts that are horizontally or vertically stacked.
/// </summary>
public static string Layout_Grid_Description {
get {
return ResourceManager.GetString("Layout_Grid_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Grid.
/// </summary>
public static string Layout_Grid_Title {
get {
return ResourceManager.GetString("Layout_Grid_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Merge zones.
/// </summary>
public static string Merge_zones {
get {
return ResourceManager.GetString("Merge_zones", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Monitor.
/// </summary>
public static string Monitor {
get {
return ResourceManager.GetString("Monitor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
@ -340,7 +520,17 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Note: Hold down Shift Key to change orientation of splitter. To merge zones, select the zones and click &quot;merge&quot;..
/// Looks up a localized string similar to Create or duplicate a layout to get started.
/// </summary>
public static string No_Custom_Layouts_Message {
get {
return ResourceManager.GetString("No_Custom_Layouts_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hold down Shift key to change orientation of splitter.
///To merge zones, select the zones and click &quot;merge&quot;..
/// </summary>
public static string Note_Custom_Table {
get {
@ -348,6 +538,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Number of zones.
/// </summary>
public static string NumberOfZones {
get {
return ResourceManager.GetString("NumberOfZones", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reset layout.
/// </summary>
@ -358,7 +557,16 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Save and apply.
/// Looks up a localized string similar to Save.
/// </summary>
public static string Save {
get {
return ResourceManager.GetString("Save", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save &amp; apply.
/// </summary>
public static string Save_Apply {
get {
@ -366,6 +574,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Template settings.
/// </summary>
public static string Settings {
get {
return ResourceManager.GetString("Settings", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show space around zones.
/// </summary>
@ -385,20 +602,11 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to Custom tab selected, press ctrl + tab to switch to Templates.
/// Looks up a localized string similar to No layout.
/// </summary>
public static string Tab_Item_Custom {
public static string Template_Layout_Blank {
get {
return ResourceManager.GetString("Tab_Item_Custom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Templates tab selected, press ctrl + tab to switch to Custom.
/// </summary>
public static string Tab_Item_Templates {
get {
return ResourceManager.GetString("Tab_Item_Templates", resourceCulture);
return ResourceManager.GetString("Template_Layout_Blank", resourceCulture);
}
}

View file

@ -132,9 +132,6 @@
<data name="Grid_Layout_Editor" xml:space="preserve">
<value>Grid layout editor</value>
</data>
<data name="Choose_Layout" xml:space="preserve">
<value>Choose your layout for this desktop</value>
</data>
<data name="Crash_Report_Message_Box_Text_Part1" xml:space="preserve">
<value>Error logged to </value>
</data>
@ -147,14 +144,11 @@
<data name="Custom_Layout_Creator" xml:space="preserve">
<value>Custom layout creator</value>
</data>
<data name="Custom_Table_Layout" xml:space="preserve">
<value>Custom table layout creator</value>
</data>
<data name="Distance_adjacent_zones" xml:space="preserve">
<value>Distance to highlight adjacent zones</value>
</data>
<data name="Edit_Selected_Layout" xml:space="preserve">
<value>Edit selected layout</value>
<data name="Edit_Layout" xml:space="preserve">
<value>Edit layout</value>
</data>
<data name="Fancy_Zones_Editor_App_Title" xml:space="preserve">
<value>FancyZones Editor</value>
@ -166,10 +160,11 @@
<value>Name</value>
</data>
<data name="Note_Custom_Table" xml:space="preserve">
<value>Note: Hold down Shift Key to change orientation of splitter. To merge zones, select the zones and click "merge".</value>
<value>Hold down Shift key to change orientation of splitter.
To merge zones, select the zones and click "merge".</value>
</data>
<data name="Save_Apply" xml:space="preserve">
<value>Save and apply</value>
<value>Save &amp; apply</value>
</data>
<data name="Show_Space_Zones" xml:space="preserve">
<value>Show space around zones</value>
@ -186,13 +181,6 @@
<data name="Zone_Count_Increment" xml:space="preserve">
<value>Increment number of zones in template layout</value>
</data>
<data name="Custom_Layout_Delete_Button" xml:space="preserve">
<value>Delete custom layout</value>
</data>
<data name="Custom_Layout_Create_New" xml:space="preserve">
<value>Create new custom</value>
<comment>As in Create new custom layout</comment>
</data>
<data name="Error_Invalid_Arguments" xml:space="preserve">
<value>FancyZones Editor arguments are invalid.</value>
</data>
@ -220,12 +208,6 @@
<data name="Template_Layout_Rows" xml:space="preserve">
<value>Rows</value>
</data>
<data name="Tab_Item_Custom" xml:space="preserve">
<value>Custom tab selected, press ctrl + tab to switch to Templates</value>
</data>
<data name="Tab_Item_Templates" xml:space="preserve">
<value>Templates tab selected, press ctrl + tab to switch to Custom</value>
</data>
<data name="Close" xml:space="preserve">
<value>Close</value>
</data>
@ -257,4 +239,99 @@
<value>Reset layout</value>
<comment>as in Reset to a blank value</comment>
</data>
<data name="Choose_Layout" xml:space="preserve">
<value>Choose the layout for this desktop</value>
</data>
<data name="Choose_layout_type" xml:space="preserve">
<value>Choose layout type</value>
<comment>Dialog header that allows the user to select a specific layout type</comment>
</data>
<data name="Create" xml:space="preserve">
<value>Create</value>
<comment>Button label that creates a new layout</comment>
</data>
<data name="Create_new_layout" xml:space="preserve">
<value>Create new layout</value>
<comment>Button label that allows the user to create a new layout</comment>
</data>
<data name="Custom_Layout_Create_New" xml:space="preserve">
<value>Custom layout</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Delete</value>
<comment>Context menu label that allows the user duplicate a layout</comment>
</data>
<data name="Duplicate" xml:space="preserve">
<value>Duplicate</value>
<comment>Context menu label that allows the user duplicate a layout</comment>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
<comment>Context menu label that allows the user edit a layout</comment>
</data>
<data name="Layout_Canvas_Description" xml:space="preserve">
<value>Create layouts that have overlapping zones</value>
</data>
<data name="Layout_Canvas_Title" xml:space="preserve">
<value>Canvas</value>
</data>
<data name="Layout_Grid_Description" xml:space="preserve">
<value>Create layouts that are horizontally or vertically stacked</value>
</data>
<data name="Layout_Grid_Title" xml:space="preserve">
<value>Grid</value>
</data>
<data name="Merge_zones" xml:space="preserve">
<value>Merge zones</value>
<comment>Button label that allows the user to merge 2 or more zones together</comment>
</data>
<data name="Monitor" xml:space="preserve">
<value>Monitor</value>
</data>
<data name="Settings" xml:space="preserve">
<value>Template settings</value>
</data>
<data name="Are_You_Sure" xml:space="preserve">
<value>Are you sure?</value>
</data>
<data name="Are_You_Sure_Description" xml:space="preserve">
<value>Are you sure you want to delete this layout?</value>
</data>
<data name="Edit_zones" xml:space="preserve">
<value>Edit zones</value>
</data>
<data name="No_Custom_Layouts_Message" xml:space="preserve">
<value>Create or duplicate a layout to get started</value>
</data>
<data name="Delete_Zone" xml:space="preserve">
<value>Delete zone</value>
<comment>A tooltip on a button that allows the user to delete a zone</comment>
</data>
<data name="Error_Parsing_Zones_Settings_Malformed_Data" xml:space="preserve">
<value>'zones-settings.json' contains malformed data.</value>
</data>
<data name="Error_Parsing_Zones_Settings_Title" xml:space="preserve">
<value>Editor settings parsing error.</value>
</data>
<data name="Error_Parsing_Zones_Settings_User_Choice" xml:space="preserve">
<value>Would you like to continue? Malformed data will be lost.</value>
</data>
<data name="Apply_Layout" xml:space="preserve">
<value>Apply layout</value>
</data>
<data name="Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="NumberOfZones" xml:space="preserve">
<value>Number of zones</value>
</data>
<data name="Template_Layout_Blank" xml:space="preserve">
<value>No layout</value>
</data>
<data name="Create_Custom_From_Template" xml:space="preserve">
<value>Create custom layout</value>
</data>
<data name="Default_Custom_Layout_Name" xml:space="preserve">
<value>Custom layout</value>
</data>
</root>

View file

@ -0,0 +1,93 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:local="clr-namespace:FancyZonesEditor.Styles">
<Style x:Key="LayoutTypeRadioButtonStyle" TargetType="RadioButton">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Background" Value="{DynamicResource RadioButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource RadioButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource RadioButtonBorderBrush}" />
<Setter Property="Padding" Value="12" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="FocusVisualStyle" Value="{DynamicResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="ui:FocusVisualHelper.UseSystemFocusVisuals" Value="{DynamicResource UseSystemFocusVisuals}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualMargin" Value="0" />
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border
x:Name="RootGrid"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="2"
SnapsToDevicePixels="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="CheckGlyph"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked" />
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Border x:Name="CheckGlyph"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
UseLayoutRounding="False"
Opacity="0"
CornerRadius="2"
BorderThickness="2"
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}" />
<ui:ContentPresenterEx x:Name="ContentPresenter"
TextElement.Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Grid.Column="1"
TextWrapping="Wrap"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ContentPresenter"
Property="Foreground"
Value="{DynamicResource RadioButtonForegroundPointerOver}" />
<Setter TargetName="CheckGlyph" Property="Opacity" Value="0.6" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ContentPresenter" Property="Foreground" Value="{DynamicResource RadioButtonForegroundPressed}" />
<Setter TargetName="RootGrid" Property="Background" Value="{DynamicResource RadioButtonBackgroundPressed}" />
<Setter TargetName="RootGrid" Property="BorderBrush" Value="{DynamicResource RadioButtonBorderBrushPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ContentPresenter" Property="Foreground" Value="{DynamicResource RadioButtonForegroundDisabled}" />
<Setter TargetName="RootGrid" Property="Background" Value="{DynamicResource RadioButtonBackgroundDisabled}" />
<Setter TargetName="RootGrid" Property="BorderBrush" Value="{DynamicResource RadioButtonBorderBrushDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View file

@ -0,0 +1,41 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:local="clr-namespace:FancyZonesEditor.Styles">
<Style x:Key="GridLayoutPreviewStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style x:Key="GridLayoutPreviewActualSizeStyle"
TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutPreviewZoneBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius"
Value="4" />
</Style>
<Style x:Key="CanvasLayoutPreviewStyle"
TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="BorderThickness" Value="10" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style x:Key="CanvasLayoutPreviewActualSizeStyle"
TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutPreviewZoneBorderBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="CornerRadius"
Value="4" />
</Style>
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">Dark.Accent1</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent1 (Dark)</system:String>
<system:String x:Key="Theme.BaseColorScheme">Dark</system:String>
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
<Color x:Key="Theme.PrimaryAccentColor">Black</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88000000" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FF3A3A3A" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FF333333" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#331B1B1B" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FF5a5a5a" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF1b1b1b"/>
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E51b1b1b" />
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent2</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent2 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent2</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF00ff00" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88000000" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FF2b2b2b" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FF333333" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#331B1B1B" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FF1b1b1b" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E51b1b1b" />
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent3</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent3 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent3</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FFc0c0c0" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88000000" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FF2b2b2b" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FF333333" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#331B1B1B" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FF1b1b1b" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E51b1b1b" />
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent4</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent4 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent4</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffffff" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF1aebff" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88000000" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FF3A3A3A" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FF333333" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#331B1B1B" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FF5a5a5a" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF1b1b1b" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E51b1b1b" />
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent5</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent5 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent5</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFf9f9f9" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFeeeeee" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFF3F3F3" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF000000" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF37006e" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88FFFFFF" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FFffffff" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FFf2f2f2" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#88cccccc" />
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#19949494" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#9c9c9c" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BFffffff" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E5ffffff" />
</ResourceDictionary>

View file

@ -0,0 +1,27 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">Light.Accent1</system:String>
<system:String x:Key="Theme.Origin">PowerToysRun</system:String>
<system:String x:Key="Theme.DisplayName">Accent1 (Light)</system:String>
<system:String x:Key="Theme.BaseColorScheme">Light</system:String>
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFF9F9F9" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFeeeeee" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFF3F3F3" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF000000" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF949494" />
<SolidColorBrush x:Key="BackdropBrush" Color="#88FFFFFF" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FFffffff"/>
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FFf2f2f2" />
<SolidColorBrush x:Key="LayoutPreviewZoneBackgroundBrush" Color="#88cccccc"/>
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#19949494" />
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#9c9c9c" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BFffffff"/>
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E5ffffff" />
</ResourceDictionary>

View file

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using ControlzEx.Theming;
namespace FancyZonesEditor.Utils
{
public class CustomLibraryThemeProvider : LibraryThemeProvider
{
public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider();
public CustomLibraryThemeProvider()
: base(true)
{
}
/// <inheritdoc />
public override void FillColorSchemeValues(Dictionary<string, string> values, RuntimeThemeColorValues colorValues)
{
}
}
}

View file

@ -0,0 +1,200 @@
// 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.Linq;
using System.Windows;
using ControlzEx.Theming;
using Microsoft.Win32;
namespace FancyZonesEditor.Utils
{
public class ThemeManager : IDisposable
{
private readonly Application _app;
private const string LightTheme = "Light.Accent1";
private const string DarkTheme = "Dark.Accent1";
private const string HighContrastOneTheme = "HighContrast.Accent2";
private const string HighContrastTwoTheme = "HighContrast.Accent3";
private const string HighContrastBlackTheme = "HighContrast.Accent4";
private const string HighContrastWhiteTheme = "HighContrast.Accent5";
private Theme currentTheme;
private bool _disposed;
public event ThemeChangedHandler ThemeChanged;
public ThemeManager(Application app)
{
_app = app;
Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml");
Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml");
Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml");
Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml");
Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml");
Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml");
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
highContrastOneThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
highContrastTwoThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
highContrastBlackThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
highContrastWhiteThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
lightThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new LibraryTheme(
darkThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ResetTheme();
ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode;
ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged;
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
}
private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SystemParameters.HighContrast))
{
ResetTheme();
}
}
public Theme GetCurrentTheme()
{
return currentTheme;
}
private static Theme GetHighContrastBaseType()
{
string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty);
theme = theme.Split('\\').Last().Split('.').First().ToString();
switch (theme)
{
case "hc1":
return Theme.HighContrastOne;
case "hc2":
return Theme.HighContrastTwo;
case "hcwhite":
return Theme.HighContrastWhite;
case "hcblack":
return Theme.HighContrastBlack;
default:
return Theme.None;
}
}
private void ResetTheme()
{
if (SystemParameters.HighContrast)
{
Theme highContrastBaseType = GetHighContrastBaseType();
ChangeTheme(highContrastBaseType);
}
else
{
string baseColor = WindowsThemeHelper.GetWindowsBaseColor();
ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor));
}
}
private void ChangeTheme(Theme theme)
{
Theme oldTheme = currentTheme;
if (theme == currentTheme)
{
return;
}
if (theme == Theme.HighContrastOne)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastOneTheme);
currentTheme = Theme.HighContrastOne;
}
else if (theme == Theme.HighContrastTwo)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastTwoTheme);
currentTheme = Theme.HighContrastTwo;
}
else if (theme == Theme.HighContrastWhite)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastWhiteTheme);
currentTheme = Theme.HighContrastWhite;
}
else if (theme == Theme.HighContrastBlack)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastBlackTheme);
currentTheme = Theme.HighContrastBlack;
}
else if (theme == Theme.Light)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, LightTheme);
currentTheme = Theme.Light;
}
else if (theme == Theme.Dark)
{
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, DarkTheme);
currentTheme = Theme.Dark;
}
else
{
currentTheme = Theme.None;
}
ThemeChanged?.Invoke(oldTheme, currentTheme);
}
private void Current_ThemeChanged(object sender, ThemeChangedEventArgs e)
{
ResetTheme();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged;
SystemParameters.StaticPropertyChanged -= SystemParameters_StaticPropertyChanged;
_disposed = true;
}
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme);
public enum Theme
{
None,
Light,
Dark,
HighContrastOne,
HighContrastTwo,
HighContrastBlack,
HighContrastWhite,
}
}

View file

@ -4,7 +4,6 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using FancyZonesEditor.Utils;
namespace FancyZonesEditor.ViewModels
@ -22,14 +21,6 @@ namespace FancyZonesEditor.ViewModels
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; }
@ -57,7 +48,7 @@ namespace FancyZonesEditor.ViewModels
double maxMultiplier = MaxPreviewDisplaySize / maxDimension;
double minMultiplier = MinPreviewDisplaySize / minDimension;
DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 2;
DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 3.5;
}
private void RaisePropertyChanged(string propertyName)

View file

@ -4,7 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800"
Title="Window1" Height="450"
Width="800"
WindowState="Maximized"
ShowInTaskbar="False"
ResizeMode="NoResize"
@ -16,7 +17,7 @@
<Grid x:Name="Body">
<Rectangle Fill="White" Opacity="0.5"/>
<Rectangle Fill="White" Opacity="1"/>
<Grid x:Name="Window1" >
<Border BorderThickness="2" BorderBrush="Black">
@ -25,7 +26,7 @@
<Thumb Background="Red" Height="36" VerticalAlignment="Top"/>
<Button Height="36" Width="36" VerticalAlignment="Top" Background="Red" FontFamily="Segoe UI Symbol" FontSize="16" Content="✖" HorizontalAlignment="Right"/>
-->
<Rectangle Fill="Black" Opacity="0.2"/>
<Rectangle Fill="Black" Opacity="1"/>
<!--
<Grid Width="24" Height="24" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,10,0,0">
<Grid.ColumnDefinitions>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 B

View file

@ -621,6 +621,12 @@ void FancyZones::ToggleEditor() noexcept
return;
}
wil::unique_cotaskmem_string virtualDesktopId;
if (!SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
{
return;
}
/*
* Divider: /
* Parts:
@ -639,13 +645,16 @@ void FancyZones::ToggleEditor() noexcept
std::wstring params;
const std::wstring divider = L"/";
params += std::to_wstring(GetCurrentProcessId()) + divider; /* Process id */
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>();
if (spanZonesAcrossMonitors)
{
params += FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get()) + divider; /* Monitor id where the Editor should be opened */
}
// device id map
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
@ -657,25 +666,15 @@ void FancyZones::ToggleEditor() noexcept
HMONITOR monitor = monitorData.first;
auto monitorInfo = monitorData.second;
std::wstring monitorId;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(monitorInfo.szDevice, displayDeviceIdxMap);
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
{
monitorId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
}
else
{
continue;
}
std::wstring monitorId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
if (monitor == targetMonitor)
if (monitor == targetMonitor && !spanZonesAcrossMonitors)
{
params += monitorId + divider; /* Monitor id where the Editor should be opened */
}
monitorsDataStr += std::move(monitorId) + divider; /* Monitor id */
UINT dpiX = 0;
UINT dpiY = 0;
if (GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
@ -690,13 +689,15 @@ void FancyZones::ToggleEditor() noexcept
prevDpiY = dpiY;
}
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.left) + divider;
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.top) + divider;
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.left) + divider; /* Top coordinate */
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.top) + divider; /* Left coordinate */
}
params += std::to_wstring(allMonitors.size()) + divider; /* Monitors count */
params += monitorsDataStr;
FancyZonesDataInstance().SaveFancyZonesEditorParameters(spanZonesAcrossMonitors, virtualDesktopId.get(), targetMonitor); /* Write parameters to json file */
if (showDpiWarning)
{
// We must show the message box in a separate thread, since this code is called from a low-level
@ -709,9 +710,6 @@ void FancyZones::ToggleEditor() noexcept
//} }.detach();
}
const auto& fancyZonesData = FancyZonesDataInstance();
fancyZonesData.SerializeDeviceInfoToTmpFile(m_currentDesktopId);
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = NonLocalizable::FZEditorExecutablePath;
@ -1304,7 +1302,7 @@ bool FancyZones::IsSplashScreen(HWND window)
void FancyZones::OnEditorExitEvent() noexcept
{
// Collect information about changes in zone layout after editor exited.
FancyZonesDataInstance().ParseDataFromTmpFiles();
FancyZonesDataInstance().LoadFancyZonesData();
UpdateZoneSets();
}

View file

@ -6,6 +6,7 @@
#include "Settings.h"
#include <common/utils/json.h>
#include <fancyzones/lib/util.h>
#include <shlwapi.h>
#include <filesystem>
@ -24,12 +25,9 @@ namespace NonLocalizable
const wchar_t FancyZonesDataFile[] = L"zones-settings.json";
const wchar_t FancyZonesAppZoneHistoryFile[] = L"app-zone-history.json";
const wchar_t FancyZonesEditorParametersFile[] = L"editor-parameters.json";
const wchar_t DefaultGuid[] = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t RegistryPath[] = L"Software\\SuperFancyZones";
const wchar_t ActiveZoneSetsTmpFileName[] = L"FancyZonesActiveZoneSets.json";
const wchar_t AppliedZoneSetsTmpFileName[] = L"FancyZonesAppliedZoneSets.json";
const wchar_t DeletedCustomZoneSetsTmpFileName[] = L"FancyZonesDeletedCustomZoneSets.json";
}
namespace
@ -151,10 +149,7 @@ FancyZonesData::FancyZonesData()
zonesSettingsFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesDataFile);
appZoneHistoryFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesAppZoneHistoryFile);
activeZoneSetTmpFileName = GetTempDirPath() + NonLocalizable::ActiveZoneSetsTmpFileName;
appliedZoneSetTmpFileName = GetTempDirPath() + NonLocalizable::AppliedZoneSetsTmpFileName;
deletedCustomZoneSetsTmpFileName = GetTempDirPath() + NonLocalizable::DeletedCustomZoneSetsTmpFileName;
editorParametersFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesEditorParametersFile);
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const std::wstring& zoneWindowId) const
@ -479,54 +474,6 @@ void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZ
}
}
void FancyZonesData::SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const
{
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, currentVirtualDesktop, activeZoneSetTmpFileName);
}
void FancyZonesData::ParseDataFromTmpFiles()
{
ParseDeviceInfoFromTmpFile(activeZoneSetTmpFileName);
ParseDeletedCustomZoneSetsFromTmpFile(deletedCustomZoneSetsTmpFileName);
ParseCustomZoneSetsFromTmpFile(appliedZoneSetTmpFileName);
SaveFancyZonesData();
}
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& appliedZonesets = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
if (appliedZonesets)
{
for (const auto& zoneset : *appliedZonesets)
{
deviceInfoMap[zoneset.first] = std::move(zoneset.second);
}
}
}
void FancyZonesData::ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& customZoneSets = JSONHelpers::ParseCustomZoneSetsFromTmpFile(tmpFilePath);
for (const auto& zoneSet : customZoneSets)
{
customZoneSetsMap[zoneSet.uuid] = zoneSet.data;
}
}
void FancyZonesData::ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& deletedCustomZoneSets = JSONHelpers::ParseDeletedCustomZoneSetsFromTmpFile(tmpFilePath);
for (const auto& zoneSet : deletedCustomZoneSets)
{
customZoneSetsMap.erase(zoneSet);
}
}
json::JsonObject FancyZonesData::GetPersistFancyZonesJSON()
{
return JSONHelpers::GetPersistFancyZonesJSON(zonesSettingsFileName, appZoneHistoryFileName);
@ -569,6 +516,69 @@ void FancyZonesData::SaveAppZoneHistory() const
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
}
void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors, const std::wstring& virtualDesktopId, const HMONITOR& targetMonitor) const
{
JSONHelpers::EditorArgs argsJson; /* json arguments */
argsJson.processId = GetCurrentProcessId(); /* Process id */
argsJson.spanZonesAcrossMonitors = spanZonesAcrossMonitors; /* Span zones */
if (spanZonesAcrossMonitors)
{
auto monitorRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
std::wstring monitorId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId);
JSONHelpers::MonitorInfo monitorJson;
monitorJson.id = monitorId;
monitorJson.top = monitorRect.top;
monitorJson.left = monitorRect.left;
monitorJson.isSelected = true;
monitorJson.dpi = 0; // unused
argsJson.monitors.emplace_back(std::move(monitorJson)); /* add monitor data */
}
else
{
std::vector<std::pair<HMONITOR, MONITORINFOEX>> allMonitors;
allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>();
// device id map for correct device ids
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
for (auto& monitorData : allMonitors)
{
HMONITOR monitor = monitorData.first;
auto monitorInfo = monitorData.second;
JSONHelpers::MonitorInfo monitorJson;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(monitorInfo.szDevice, displayDeviceIdxMap);
std::wstring monitorId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId);
if (monitor == targetMonitor)
{
monitorJson.isSelected = true; /* Is monitor selected for the main editor window opening */
}
monitorJson.id = monitorId; /* Monitor id */
UINT dpiX = 0;
UINT dpiY = 0;
if (GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
{
monitorJson.dpi = dpiX; /* DPI */
}
monitorJson.top = monitorInfo.rcMonitor.top; /* Top coordinate */
monitorJson.left = monitorInfo.rcMonitor.left; /* Left coordinate */
argsJson.monitors.emplace_back(std::move(monitorJson)); /* add monitor data */
}
}
json::to_file(editorParametersFileName, JSONHelpers::EditorArgs::ToJson(argsJson));
}
void FancyZonesData::RemoveDesktopAppZoneHistory(const std::wstring& desktopId)
{
for (auto it = std::begin(appZoneHistoryMap); it != std::end(appZoneHistoryMap);)

View file

@ -78,9 +78,6 @@ public:
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
void SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const;
void ParseDataFromTmpFiles();
json::JsonObject GetPersistFancyZonesJSON();
void LoadFancyZonesData();
@ -88,6 +85,8 @@ public:
void SaveZoneSettings() const;
void SaveAppZoneHistory() const;
void SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors, const std::wstring& virtualDesktopId, const HMONITOR& targetMonitor) const;
private:
#if defined(UNIT_TESTS)
friend class FancyZonesUnitTests::FancyZonesDataUnitTests;
@ -101,6 +100,11 @@ private:
deviceInfoMap[deviceId] = data;
}
inline void SetCustomZonesets(const std::wstring& uuid, FancyZonesDataTypes::CustomZoneSetData data)
{
customZoneSetsMap[uuid] = data;
}
inline bool ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
{
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
@ -121,10 +125,6 @@ private:
appZoneHistoryFileName = result + L"\\" + std::wstring(L"app-zone-history.json");
}
#endif
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
void ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
void ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
// Maps app path to app's zone history data
@ -136,10 +136,7 @@ private:
std::wstring zonesSettingsFileName;
std::wstring appZoneHistoryFileName;
std::wstring activeZoneSetTmpFileName;
std::wstring appliedZoneSetTmpFileName;
std::wstring deletedCustomZoneSetsTmpFileName;
std::wstring editorParametersFileName;
mutable std::recursive_mutex dataLock;
};

View file

@ -45,6 +45,7 @@ namespace FancyZonesDataTypes
int height;
};
std::vector<CanvasLayoutInfo::Rect> zones;
int sensitivityRadius;
};
struct GridLayoutInfo
@ -62,6 +63,9 @@ namespace FancyZonesDataTypes
const std::vector<int>& rowsPercents;
const std::vector<int>& columnsPercents;
const std::vector<std::vector<int>>& cellChildMap;
bool showSpacing;
int spacing;
int sensitivityRadius;
};
GridLayoutInfo(const Minimal& info);
@ -74,16 +78,22 @@ namespace FancyZonesDataTypes
inline int rows() const { return m_rows; }
inline int columns() const { return m_columns; }
inline const std::vector<int>& rowsPercents() const { return m_rowsPercents; };
inline const std::vector<int>& columnsPercents() const { return m_columnsPercents; };
inline const std::vector<std::vector<int>>& cellChildMap() const { return m_cellChildMap; };
inline bool showSpacing() const { return m_showSpacing; }
inline int spacing() const { return m_spacing; }
inline int sensitivityRadius() const { return m_sensitivityRadius; }
int m_rows;
int m_columns;
std::vector<int> m_rowsPercents;
std::vector<int> m_columnsPercents;
std::vector<std::vector<int>> m_cellChildMap;
bool m_showSpacing;
int m_spacing;
int m_sensitivityRadius;
};
struct CustomZoneSetData

View file

@ -17,16 +17,13 @@
namespace NonLocalizable
{
const wchar_t ActiveZoneSetStr[] = L"active-zoneset";
const wchar_t AppliedZonesets[] = L"applied-zonesets";
const wchar_t AppPathStr[] = L"app-path";
const wchar_t AppZoneHistoryStr[] = L"app-zone-history";
const wchar_t CanvasStr[] = L"canvas";
const wchar_t CellChildMapStr[] = L"cell-child-map";
const wchar_t ColumnsPercentageStr[] = L"columns-percentage";
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 DeletedCustomZoneSetsStr[] = L"deleted-custom-zone-sets";
const wchar_t DeviceIdStr[] = L"device-id";
const wchar_t DevicesStr[] = L"devices";
const wchar_t EditorShowSpacingStr[] = L"editor-show-spacing";
@ -42,6 +39,10 @@ namespace NonLocalizable
const wchar_t RefWidthStr[] = L"ref-width";
const wchar_t RowsPercentageStr[] = L"rows-percentage";
const wchar_t RowsStr[] = L"rows";
const wchar_t SensitivityRadius[] = L"sensitivity-radius";
const wchar_t ShowSpacing[] = L"show-spacing";
const wchar_t Spacing[] = L"spacing";
const wchar_t Templates[] = L"templates";
const wchar_t TypeStr[] = L"type";
const wchar_t UuidStr[] = L"uuid";
const wchar_t WidthStr[] = L"width";
@ -51,6 +52,16 @@ namespace NonLocalizable
const wchar_t ZoneIndexStr[] = L"zone-index";
const wchar_t ZoneSetUuidStr[] = L"zoneset-uuid";
const wchar_t ZonesStr[] = L"zones";
// Editor arguments
const wchar_t Dpi[] = L"dpi";
const wchar_t MonitorId[] = L"monitor-id";
const wchar_t TopCoordinate[] = L"top-coordinate";
const wchar_t LeftCoordinate[] = L"left-coordinate";
const wchar_t IsSelected[] = L"is-selected";
const wchar_t ProcessId[] = L"process-id";
const wchar_t SpanZonesAcrossMonitors[] = L"span-zones-across-monitors";
const wchar_t Monitors[] = L"monitors";
}
namespace
@ -130,6 +141,7 @@ namespace JSONHelpers
zonesJson.Append(zoneJson);
}
infoJson.SetNamedValue(NonLocalizable::ZonesStr, zonesJson);
infoJson.SetNamedValue(NonLocalizable::SensitivityRadius, json::value(canvasInfo.sensitivityRadius));
return infoJson;
}
@ -154,6 +166,8 @@ namespace JSONHelpers
FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height };
info.zones.push_back(zone);
}
info.sensitivityRadius = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::SensitivityRadius, DefaultValues::SensitivityRadius));
return info;
}
catch (const winrt::hresult_error&)
@ -176,6 +190,10 @@ namespace JSONHelpers
cellChildMapJson.Append(NumVecToJsonArray(gridInfo.m_cellChildMap[i]));
}
infoJson.SetNamedValue(NonLocalizable::CellChildMapStr, cellChildMapJson);
infoJson.SetNamedValue(NonLocalizable::SensitivityRadius, json::value(gridInfo.m_sensitivityRadius));
infoJson.SetNamedValue(NonLocalizable::ShowSpacing, json::value(gridInfo.m_showSpacing));
infoJson.SetNamedValue(NonLocalizable::Spacing, json::value(gridInfo.m_spacing));
return infoJson;
}
@ -210,6 +228,10 @@ namespace JSONHelpers
info.cellChildMap().push_back(JsonArrayToNumVec(cellsArray));
}
info.m_showSpacing = infoJson.GetNamedBoolean(NonLocalizable::ShowSpacing, DefaultValues::ShowSpacing);
info.m_spacing = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::Spacing, DefaultValues::Spacing));
info.m_sensitivityRadius = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::SensitivityRadius, DefaultValues::SensitivityRadius));
return info;
}
catch (const winrt::hresult_error&)
@ -449,64 +471,37 @@ namespace JSONHelpers
}
}
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap)
json::JsonObject MonitorInfo::ToJson(const MonitorInfo& monitor)
{
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::Dpi, json::value(monitor.dpi));
result.SetNamedValue(NonLocalizable::MonitorId, json::value(monitor.id));
result.SetNamedValue(NonLocalizable::TopCoordinate, json::value(monitor.top));
result.SetNamedValue(NonLocalizable::LeftCoordinate, json::value(monitor.left));
result.SetNamedValue(NonLocalizable::IsSelected, json::value(monitor.isSelected));
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
return result;
}
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop)
json::JsonObject EditorArgs::ToJson(const EditorArgs& args)
{
json::JsonObject result{};
json::JsonArray array;
for (const auto& info : deviceInfoMap)
result.SetNamedValue(NonLocalizable::ProcessId, json::value(args.processId));
result.SetNamedValue(NonLocalizable::SpanZonesAcrossMonitors, json::value(args.spanZonesAcrossMonitors));
json::JsonArray monitors;
for (const auto& monitor : args.monitors)
{
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));
}
monitors.Append(MonitorInfo::ToJson(monitor));
}
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
result.SetNamedValue(NonLocalizable::Monitors, monitors);
return result;
}
std::optional<TDeviceInfoMap> AppliedZonesetsJSON::FromJson(const json::JsonObject& json)
{
try
{
TDeviceInfoMap 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)
{
auto result = json::from_file(zonesSettingsFileName);
@ -534,12 +529,27 @@ namespace JSONHelpers
void SaveZoneSettings(const std::wstring& zonesSettingsFileName, const TDeviceInfoMap& deviceInfoMap, const TCustomZoneSetsMap& customZoneSetsMap)
{
json::JsonObject root{};
auto before = json::from_file(zonesSettingsFileName);
json::JsonObject root{};
json::JsonArray templates{};
try
{
if (before.has_value() && before->HasKey(NonLocalizable::Templates))
{
templates = before->GetNamedArray(NonLocalizable::Templates);
}
}
catch (const winrt::hresult_error&)
{
}
root.SetNamedValue(NonLocalizable::DevicesStr, JSONHelpers::SerializeDeviceInfos(deviceInfoMap));
root.SetNamedValue(NonLocalizable::CustomZoneSetsStr, JSONHelpers::SerializeCustomZoneSets(customZoneSetsMap));
auto before = json::from_file(zonesSettingsFileName);
root.SetNamedValue(NonLocalizable::Templates, templates);
if (!before.has_value() || before.value().Stringify() != root.Stringify())
{
Trace::FancyZones::DataChanged();
@ -665,104 +675,4 @@ namespace JSONHelpers
return customZoneSetsJSON;
}
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath)
{
json::to_file(tmpFilePath, JSONHelpers::AppliedZonesetsJSON::ToJson(deviceInfoMap, currentVirtualDesktop));
}
void SerializeCustomZoneSetsToTmpFile(const TCustomZoneSetsMap& customZoneSetsMap, std::wstring_view tmpFilePath)
{
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 (auto zoneSetJson = json::from_file(tmpFilePath); zoneSetJson.has_value())
{
if (auto deviceInfo = JSONHelpers::AppliedZonesetsJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
{
result = std::move(deviceInfo);
}
else
{
Logger::trace(L"ParseDeviceInfoFromTmpFile: AppliedZonesetsJSON::FromJson parsing error, {}", zoneSetJson.value().Stringify());
}
}
}
DeleteTmpFile(tmpFilePath);
return result;
}
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
{
std::vector<CustomZoneSetJSON> result;
if (std::filesystem::exists(tmpFilePath))
{
try
{
if (auto customZoneSetJson = json::from_file(tmpFilePath); customZoneSetJson.has_value())
{
auto zoneSetArray = customZoneSetJson.value().GetNamedArray(NonLocalizable::CreatedCustomZoneSets);
for (const auto& zoneSet : zoneSetArray)
{
if (auto customZoneSet = JSONHelpers::CustomZoneSetJSON::FromJson(zoneSet.GetObjectW()); customZoneSet.has_value())
{
result.emplace_back(std::move(*customZoneSet));
}
else
{
Logger::trace(L"ParseCustomZoneSetsFromTmpFile: CustomZoneSetJSON::FromJson parsing error, {}", zoneSet.GetObjectW().Stringify());
}
}
}
}
catch (const winrt::hresult_error& err)
{
Logger::trace(L"ParseCustomZoneSetsFromTmpFile: CustomZoneSetJSON::FromJson parsing error, {}", err.message());
}
DeleteTmpFile(tmpFilePath);
}
return result;
}
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
{
std::vector<std::wstring> result{};
if (std::filesystem::exists(tmpFilePath))
{
auto deletedZoneSetsJson = json::from_file(tmpFilePath);
try
{
auto deletedCustomZoneSets = deletedZoneSetsJson->GetNamedArray(NonLocalizable::DeletedCustomZoneSetsStr);
for (auto zoneSet : deletedCustomZoneSets)
{
std::wstring uuid = L"{" + std::wstring{ zoneSet.GetString() } + L"}";
result.push_back(uuid);
}
}
catch (const winrt::hresult_error&)
{
}
DeleteTmpFile(tmpFilePath);
}
return result;
}
}

View file

@ -59,11 +59,24 @@ namespace JSONHelpers
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
struct AppliedZonesetsJSON
struct MonitorInfo
{
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);
int dpi;
std::wstring id;
int top;
int left;
bool isSelected = false;
static json::JsonObject ToJson(const MonitorInfo& monitor);
};
struct EditorArgs
{
DWORD processId;
bool spanZonesAcrossMonitors;
std::vector<MonitorInfo> monitors;
static json::JsonObject ToJson(const EditorArgs& args);
};
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
@ -79,14 +92,4 @@ namespace JSONHelpers
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath);
std::optional<TDeviceInfoMap> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(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
}

View file

@ -189,7 +189,8 @@ namespace FancyZonesUnitTests
TEST_CLASS (CanvasLayoutInfoUnitTests)
{
json::JsonObject m_json = json::JsonObject::Parse(L"{\"ref-width\": 123, \"ref-height\": 321, \"zones\": [{\"X\": 11, \"Y\": 22, \"width\": 33, \"height\": 44}, {\"X\": 55, \"Y\": 66, \"width\": 77, \"height\": 88}]}");
json::JsonObject m_json = json::JsonObject::Parse(L"{\"ref-width\": 123, \"ref-height\": 321, \"zones\": [{\"X\": 11, \"Y\": 22, \"width\": 33, \"height\": 44}, {\"X\": 55, \"Y\": 66, \"width\": 77, \"height\": 88}], \"sensitivity-radius\": 50}");
json::JsonObject m_jsonWithoutOptionalValues = json::JsonObject::Parse(L"{\"ref-width\": 123, \"ref-height\": 321, \"zones\": [{\"X\": 11, \"Y\": 22, \"width\": 33, \"height\": 44}, {\"X\": 55, \"Y\": 66, \"width\": 77, \"height\": 88}]}");
TEST_METHOD (ToJson)
{
@ -197,6 +198,7 @@ namespace FancyZonesUnitTests
info.lastWorkAreaWidth = 123;
info.lastWorkAreaHeight = 321;
info.zones = { CanvasLayoutInfo::Rect{ 11, 22, 33, 44 }, CanvasLayoutInfo::Rect{ 55, 66, 77, 88 } };
info.sensitivityRadius = 50;
auto actual = CanvasLayoutInfoJSON::ToJson(info);
compareJsonObjects(m_json, actual);
@ -208,6 +210,7 @@ namespace FancyZonesUnitTests
expected.lastWorkAreaWidth = 123;
expected.lastWorkAreaHeight = 321;
expected.zones = { CanvasLayoutInfo::Rect{ 11, 22, 33, 44 }, CanvasLayoutInfo::Rect{ 55, 66, 77, 88 } };
expected.sensitivityRadius = 50;
auto actual = CanvasLayoutInfoJSON::FromJson(m_json);
Assert::IsTrue(actual.has_value());
@ -215,6 +218,30 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected.lastWorkAreaHeight, actual->lastWorkAreaHeight);
Assert::AreEqual(expected.lastWorkAreaWidth, actual->lastWorkAreaWidth);
Assert::AreEqual(expected.zones.size(), actual->zones.size());
Assert::AreEqual(expected.sensitivityRadius, actual->sensitivityRadius);
for (int i = 0; i < expected.zones.size(); i++)
{
Assert::AreEqual(expected.zones[i].x, actual->zones[i].x);
Assert::AreEqual(expected.zones[i].y, actual->zones[i].y);
Assert::AreEqual(expected.zones[i].width, actual->zones[i].width);
Assert::AreEqual(expected.zones[i].height, actual->zones[i].height);
}
}
TEST_METHOD (FromJsonWithoutOptionalValues)
{
CanvasLayoutInfo expected;
expected.lastWorkAreaWidth = 123;
expected.lastWorkAreaHeight = 321;
expected.zones = { CanvasLayoutInfo::Rect{ 11, 22, 33, 44 }, CanvasLayoutInfo::Rect{ 55, 66, 77, 88 } };
auto actual = CanvasLayoutInfoJSON::FromJson(m_jsonWithoutOptionalValues);
Assert::IsTrue(actual.has_value());
Assert::AreEqual(expected.lastWorkAreaHeight, actual->lastWorkAreaHeight);
Assert::AreEqual(expected.lastWorkAreaWidth, actual->lastWorkAreaWidth);
Assert::AreEqual(expected.zones.size(), actual->zones.size());
Assert::AreEqual(DefaultValues::SensitivityRadius, actual->sensitivityRadius);
for (int i = 0; i < expected.zones.size(); i++)
{
Assert::AreEqual(expected.zones[i].x, actual->zones[i].x);
@ -226,7 +253,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (FromJsonMissingKeys)
{
CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 11, 22, 33, 44 }, CanvasLayoutInfo::Rect{ 55, 66, 77, 88 } } };
CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 11, 22, 33, 44 }, CanvasLayoutInfo::Rect{ 55, 66, 77, 88 } }, 50 };
const auto json = CanvasLayoutInfoJSON::ToJson(info);
auto iter = json.First();
@ -234,6 +261,11 @@ namespace FancyZonesUnitTests
{
json::JsonObject modifiedJson = json::JsonObject::Parse(json.Stringify());
modifiedJson.Remove(iter.Current().Key());
if (iter.Current().Key() == L"sensitivity-radius")
{
iter.MoveNext();
continue;
}
auto actual = CanvasLayoutInfoJSON::FromJson(modifiedJson);
Assert::IsFalse(actual.has_value());
@ -436,6 +468,27 @@ namespace FancyZonesUnitTests
compareJsonObjects(expected, actual);
}
TEST_METHOD (ToJsonWithOptionals)
{
json::JsonObject expected = json::JsonObject();
expected = json::JsonObject::Parse(L"{\"rows\": 3, \"columns\": 4}");
expected.SetNamedValue(L"rows-percentage", m_rowsArray);
expected.SetNamedValue(L"columns-percentage", m_columnsArray);
expected.SetNamedValue(L"cell-child-map", m_cells);
expected.SetNamedValue(L"show-spacing", json::value(true));
expected.SetNamedValue(L"spacing", json::value(99));
expected.SetNamedValue(L"sensitivity-radius", json::value(55));
GridLayoutInfo info = m_info;
info.m_sensitivityRadius = 55;
info.m_showSpacing = true;
info.m_spacing = 99;
auto actual = GridLayoutInfoJSON::ToJson(info);
compareJsonObjects(expected, actual);
}
TEST_METHOD (FromJson)
{
json::JsonObject json = json::JsonObject(m_gridJson);
@ -514,6 +567,27 @@ namespace FancyZonesUnitTests
}
}
TEST_METHOD(FromJsonWithOptionals)
{
json::JsonObject json = json::JsonObject();
json = json::JsonObject::Parse(L"{\"rows\": 3, \"columns\": 4}");
json.SetNamedValue(L"rows-percentage", m_rowsArray);
json.SetNamedValue(L"columns-percentage", m_columnsArray);
json.SetNamedValue(L"cell-child-map", m_cells);
json.SetNamedValue(L"show-spacing", json::value(true));
json.SetNamedValue(L"spacing", json::value(99));
json.SetNamedValue(L"sensitivity-radius", json::value(55));
GridLayoutInfo expected = m_info;
expected.m_sensitivityRadius = 55;
expected.m_showSpacing = true;
expected.m_spacing = 99;
auto actual = GridLayoutInfoJSON::FromJson(json);
Assert::IsTrue(actual.has_value());
compareGridInfos(expected, *actual);
}
TEST_METHOD (FromJsonInvalidTypes)
{
json::JsonObject gridJson = json::JsonObject::Parse(L"{\"rows\": \"три\", \"columns\": \"четыре\"}");
@ -938,92 +1012,6 @@ 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)
{
private:
@ -1186,74 +1174,6 @@ namespace FancyZonesUnitTests
compareJsonArrays(expectedDevices, actual);
}
TEST_METHOD (DeviceInfoSaveTemp)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
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";
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_defaultVDId, path);
bool actualFileExists = std::filesystem::exists(path);
Assert::IsTrue(actualFileExists);
auto expectedData = AppliedZonesetsJSON::ToJson(deviceInfoMap);
auto actualSavedData = json::from_file(path);
std::filesystem::remove(path); //clean up before compare asserts
Assert::IsTrue(actualSavedData.has_value());
compareJsonObjects(expectedData, *actualSavedData);
}
TEST_METHOD (DeviceInfoReadTemp)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring deviceId = m_defaultDeviceId;
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";
JSONHelpers::SerializeDeviceInfoToTmpFile(expectedDeviceInfoMap, m_defaultVDId, path);
data.ParseDeviceInfoFromTmpFile(path);
bool actualFileExists = std::filesystem::exists(path);
if (actualFileExists)
{
std::filesystem::remove(path); //clean up before compare asserts
}
Assert::IsFalse(actualFileExists);
auto devices = data.GetDeviceInfoMap();
Assert::AreEqual((size_t)1, devices.size());
auto actual = devices.find(deviceId)->second;
Assert::AreEqual(expected.showSpacing, actual.showSpacing);
Assert::AreEqual(expected.spacing, actual.spacing);
Assert::AreEqual(expected.zoneCount, actual.zoneCount);
Assert::AreEqual((int)expected.activeZoneSet.type, (int)actual.activeZoneSet.type);
Assert::AreEqual(expected.activeZoneSet.uuid.c_str(), actual.activeZoneSet.uuid.c_str());
}
TEST_METHOD (DeviceInfoReadTempNonexistent)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
data.ParseDeviceInfoFromTmpFile(path);
auto devices = data.GetDeviceInfoMap();
Assert::AreEqual((size_t)0, devices.size());
}
TEST_METHOD (AppZoneHistoryParseSingle)
{
const std::wstring expectedAppPath = L"appPath";
@ -1619,72 +1539,6 @@ namespace FancyZonesUnitTests
compareJsonArrays(expected, actual);
}
TEST_METHOD (CustomZoneSetsReadTemp)
{
//prepare device data
const std::wstring deviceId = L"default_device_id";
{
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";
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_defaultVDId, deviceInfoPath);
m_fzData.ParseDeviceInfoFromTmpFile(deviceInfoPath);
std::filesystem::remove(deviceInfoPath);
}
const std::wstring uuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
const GridLayoutInfo grid(GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 3,
.rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }));
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Grid, grid };
CustomZoneSetJSON expected{ uuid, zoneSetData };
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring path = data.zonesSettingsFileName + L".test_tmp";
TCustomZoneSetsMap customZoneSets;
customZoneSets.insert(std::make_pair(uuid, zoneSetData));
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, path);
m_fzData.ParseCustomZoneSetsFromTmpFile(path);
bool actualFileExists = std::filesystem::exists(path);
if (actualFileExists)
{
std::filesystem::remove(path); //clean up before compare asserts
}
Assert::IsFalse(actualFileExists);
auto devices = m_fzData.GetCustomZoneSetsMap();
Assert::AreEqual((size_t)1, devices.size());
auto actual = devices.find(uuid)->second;
Assert::AreEqual((int)expected.data.type, (int)actual.type);
Assert::AreEqual(expected.data.name.c_str(), actual.name.c_str());
auto expectedGrid = std::get<GridLayoutInfo>(expected.data.info);
auto actualGrid = std::get<GridLayoutInfo>(actual.info);
Assert::AreEqual(expectedGrid.rows(), actualGrid.rows());
Assert::AreEqual(expectedGrid.columns(), actualGrid.columns());
}
TEST_METHOD (CustomZoneSetsReadTempNonexistent)
{
const std::wstring path = m_fzData.zonesSettingsFileName + L".test_tmp";
const std::wstring deviceId = L"default_device_id";
m_fzData.ParseCustomZoneSetsFromTmpFile(path);
auto devices = m_fzData.GetDeviceInfoMap();
Assert::AreEqual((size_t)0, devices.size());
}
TEST_METHOD (SetActiveZoneSet)
{
FancyZonesData data;
@ -1879,6 +1733,36 @@ namespace FancyZonesUnitTests
Assert::IsTrue(actual);
}
TEST_METHOD (SaveFancyZonesDataWithTemplates)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
// json with templates
json::JsonObject expectedJsonObj;
json::JsonObject templateObj = json::JsonObject::Parse(L"{\"type\": \"focus\", \"show-spacing\": false, \"spacing\": 15, \"zone-count\": 7, \"sensitivity-radius\": 25}");
json::JsonArray templatesArray{};
templatesArray.Append(templateObj);
expectedJsonObj.SetNamedValue(L"devices", json::JsonArray{});
expectedJsonObj.SetNamedValue(L"custom-zone-sets", json::JsonArray{});
expectedJsonObj.SetNamedValue(L"templates", templatesArray);
// write json with templates to file
json::to_file(jsonPath, expectedJsonObj);
data.SaveFancyZonesData();
// verify that file was written successfully
Assert::IsTrue(std::filesystem::exists(jsonPath));
// verify that templates were not changed after calling SaveFancyZonesData()
std::wstring str;
std::wifstream { jsonPath, std::ios::binary } >> str;
json::JsonObject actualJson = json::JsonObject::Parse(str);
compareJsonObjects(expectedJsonObj, actualJson);
}
TEST_METHOD (AppLastZoneIndex)
{
const std::wstring deviceId = L"device-id";
@ -2067,4 +1951,38 @@ namespace FancyZonesUnitTests
Assert::IsFalse(data.RemoveAppLastZone(nullptr, deviceId, zoneSetId));
}
};
TEST_CLASS(EditorArgsUnitTests)
{
TEST_METHOD(MonitorToJson)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
MonitorInfo monitor{ 144, deviceId, -10, 0, true };
const auto expectedStr = L"{\"dpi\": 144, \"monitor-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"top-coordinate\": -10, \"left-coordinate\": 0, \"is-selected\": true}";
const auto expected = json::JsonObject::Parse(expectedStr);
const auto actual = MonitorInfo::ToJson(monitor);
compareJsonObjects(expected, actual);
}
TEST_METHOD(EditorArgsToJson)
{
MonitorInfo monitor1{ 144, L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", -10, 0, true };
MonitorInfo monitor2{ 96, L"AOC2460#4&fe3a015&0&UID65793_1920_1080_{39B25DD2-130D-4B5D-8851-4791D66B1538}", 0, 1920, false };
EditorArgs args{
1, true, std::vector<MonitorInfo>{ monitor1, monitor2 }
};
const std::wstring expectedMonitor1 = L"{\"dpi\": 144, \"monitor-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"top-coordinate\": -10, \"left-coordinate\": 0, \"is-selected\": true}";
const std::wstring expectedMonitor2 = L"{\"dpi\": 96, \"monitor-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1080_{39B25DD2-130D-4B5D-8851-4791D66B1538}\", \"top-coordinate\": 0, \"left-coordinate\": 1920, \"is-selected\": false}";
const std::wstring expectedStr = L"{\"process-id\": 1, \"span-zones-across-monitors\": true, \"monitors\": [" + expectedMonitor1 + L", " + expectedMonitor2 + L"]}";
const auto expected = json::JsonObject::Parse(expectedStr);
const auto actual = EditorArgs::ToJson(args);
compareJsonObjects(expected, actual);
}
};
}

View file

@ -1047,34 +1047,16 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidCanvasLayoutInfo)
{
//prepare device data
{
const std::wstring zoneUuid = L"default_device_id";
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";
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, virtualDesktopId, deviceInfoPath);
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
std::filesystem::remove(deviceInfoPath);
}
const std::wstring zoneUuid = L"default_device_id";
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
//prepare expected data
wil::unique_cotaskmem_string 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 } } };
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Canvas, info };
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));
FancyZonesDataInstance().ParseCustomZoneSetsFromTmpFile(m_path);
FancyZonesDataInstance().SetCustomZonesets(uuid.get(), zoneSetData);
//test
const int spacing = 10;
const int zoneCount = static_cast<int>(info.zones.size());
@ -1091,20 +1073,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidGridFullLayoutInfo)
{
//prepare device data
{
const std::wstring zoneUuid = L"default_device_id";
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";
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, virtualDesktopId, deviceInfoPath);
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
std::filesystem::remove(deviceInfoPath);
}
const std::wstring zoneUuid = L"default_device_id";
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
//prepare expected data
wil::unique_cotaskmem_string uuid;
@ -1116,13 +1086,7 @@ namespace FancyZonesUnitTests
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }));
CustomZoneSetData zoneSetData{ L"name", CustomLayoutType::Grid, grid };
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));
FancyZonesDataInstance().ParseCustomZoneSetsFromTmpFile(m_path);
FancyZonesDataInstance().SetCustomZonesets(uuid.get(), zoneSetData);
const int spacing = 10;
const int zoneCount = grid.rows() * grid.columns();

View file

@ -175,225 +175,9 @@ namespace FancyZonesUnitTests
Assert::AreEqual(activeZoneSet->GetZones().size(), static_cast<size_t>(3));
}
TEST_METHOD (CreateZoneWindowWithActiveZoneTmpFile)
{
using namespace FancyZonesDataTypes;
const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), static_cast<ZoneSetLayoutType>(type) };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
}
}
TEST_METHOD (CreateZoneWindowWithActiveCustomZoneTmpFile)
{
using namespace FancyZonesDataTypes;
const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
JSONHelpers::TDeviceInfoMap deviceInfoMap;
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
testZoneWindow(actual);
//custom zone needs temp file for applied zone
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)0, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowWithActiveCustomZoneAppliedTmpFile)
{
using namespace FancyZonesDataTypes;
//save required data
const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
JSONHelpers::TDeviceInfoMap deviceInfoMap;
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData });
JSONHelpers::TCustomZoneSetsMap customZoneSets;
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
testZoneWindow(actual);
//custom zone needs temp file for applied zone
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithDeletedCustomZones)
{
using namespace FancyZonesDataTypes;
//save required data
const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const auto deletedZonesTempPath = m_fancyZonesData.deletedCustomZoneSetsTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
JSONHelpers::TDeviceInfoMap deviceInfoMap;
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
JSONHelpers::TCustomZoneSetsMap customZoneSets;
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
//save same zone as deleted
json::JsonObject deletedCustomZoneSets = {};
json::JsonArray zonesArray{};
zonesArray.Append(json::JsonValue::CreateStringValue(customZoneSet.uuid.substr(1, customZoneSet.uuid.size() - 2).c_str()));
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithUnusedDeletedCustomZones)
{
using namespace FancyZonesDataTypes;
//save required data
const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const auto deletedZonesTempPath = m_fancyZonesData.deletedCustomZoneSetsTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
JSONHelpers::TDeviceInfoMap deviceInfoMap;
deviceInfoMap.insert(std::make_pair(m_uniqueId.str(), data));
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, m_virtualDesktopGuid, activeZoneSetTempPath);
const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
};
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
JSONHelpers::TCustomZoneSetsMap customZoneSets;
customZoneSets.insert(std::make_pair(customSetGuid, customZoneData));
JSONHelpers::SerializeCustomZoneSetsToTmpFile(customZoneSets, appliedZoneSetTempPath);
//save different zone as deleted
json::JsonObject deletedCustomZoneSets = {};
json::JsonArray zonesArray{};
const auto uuid = Helpers::CreateGuidString();
zonesArray.Append(json::JsonValue::CreateStringValue(uuid.substr(1, uuid.size() - 2).c_str()));
deletedCustomZoneSets.SetNamedValue(L"deleted-custom-zone-sets", zonesArray);
json::to_file(deletedZonesTempPath, deletedCustomZoneSets);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseDeletedCustomZoneSetsFromTmpFile(deletedZonesTempPath);
m_fancyZonesData.ParseCustomZoneSetsFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), {});
testZoneWindow(actual);
Assert::IsNotNull(actual->ActiveZoneSet());
const auto actualZoneSet = actual->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowClonedFromParent)
{
using namespace FancyZonesDataTypes;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
winrt::com_ptr<MockZoneWindowHost> zoneWindowHost = winrt::make_self<MockZoneWindowHost>();
auto parentZoneWindow = MakeZoneWindow(zoneWindowHost.get(), m_hInst, m_monitor, m_parentUniqueId.str(), {});
zoneWindowHost->m_zoneWindow = parentZoneWindow.get();
// newWorkArea = true - zoneWindow will be cloned from parent
auto actualZoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().get(), m_hInst, m_monitor, m_uniqueId.str(), m_parentUniqueId.str());
Assert::IsNotNull(actualZoneWindow->ActiveZoneSet());
const auto actualZoneSet = actualZoneWindow->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)zoneCount, actualZoneSet.size());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
Assert::AreEqual(zoneCount, currentDeviceInfo.zoneCount);
Assert::AreEqual(spacing, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(type), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD (CreateZoneWindowNotClonedFromParent)
{
using namespace FancyZonesDataTypes;
TEST_METHOD (CreateZoneWindowClonedFromParent)
{
using namespace FancyZonesDataTypes;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;